Australian (ASX) Stock Market Forum

AmiBroker > Backtesting > BuyPrice is not the entry price?

Joined
28 August 2011
Posts
7
Reactions
0
Dear all,

I've been backtesting with amibroker for the past few days and I just hit a wall in my simple coding.

I am using 15 days of intraday data (15min).

Buy = Cross(RSI(C,6),15);
Sell = Cross(85,RSI(C,6) AND Ref(Close,-1)>BuyPrice*1.01;

1. One of my two sell condition is to have the exit price > entry price*1.01. In my results some exit prices are still below the entry prices and I've no idea why For example it bought at 5.03 and sold at 4.25!

I am starting to guess that the BuyPrice is not the entry price of my actual trade (5.03)

2. It is possible to test 5min intraday? In the settings periodicity I see only 15min and 20 min?

Any help will be greatly appreciated,
Cheers,

Chris
 
Hi Chris --

Welcome to AmiBroker. One feature of AmiBroker's AFL language is that many of the operators take arrays as operands and return arrays as results. A variable such as Buy or BuyPrice has a value for every bar.

For example, examine this statement:
Buy = Cross(RSI(C,6),15);

RSI(C,6) is a function that acts as an array operation. It accepts two arguments whose values are C and 6 for this example.

C, or Close, is an array and it has a value for every bar. If you are using 100 days of end-of-day data, C is an array 100 elements in length.

6 is a scalar, a single value, and in this case a constant whose value is 6.

When RSI(C,6) is evaluated, it returns an array that is also 100 elements in length. For each element the value is the RSI using the 6 Closing prices preceding and including the current Close. Importantly, all 100 elements are computed with the single call to the RSI function. There is a value for each of the 100 elements, and they are all the value you expect them to be.

The Cross function is also an array operation. It is True on those bars, and only those bars, where on the previous bar the value of the first operand is less than the value of the second operand, and on the current bar the value of the first operand is greater than the value of the second operand. That is, it returns True on those bars when the first operand crosses up through the second operand. It returns False for all other bars.

On those bars, and only on those bars, when the previous value of RSI(C,6) was less than 15 and the current value of RSI(C,6) is greater than 15, the value of the Buy array is set to True (or 1). For all other bars, the value of Buy is False (or 0 -- actually anything other than 1).

When the backtester makes its first pass, it goes through the Buy array and signals entry into a long position whenever it encounters a value of True. The code you posted does not indicate the BuyPrice or TradeDelay you are using. Assuming BuyPrice = Close and TradeDelay = 0, a long position is entered at the close of the signal bar using the Close as the entry price. For that bar, BuyPrice = Close.

Part of the problem you are having is that BuyPrice is set to some value for every bar. Unless you explicitly refer to the value that BuyPrice had on the bar of entry, the value returned by a reference to BuyPrice will probably not be the value you want. To specify the specific BuyPrice, use a construction such as:
EntryPrice = ValueWhen(Buy,BuyPrice);

But the code you posted has another problem.
The line you posted has a syntax error and will not compile correctly. There is an imbalance in parentheses.
Sell = Cross(85,RSI(C,6) AND Ref(Close,-1)>BuyPrice*1.01;
I'll assume it should be:
Sell = Cross(85,RSI(C,6)) AND Ref(Close,-1)>BuyPrice*1.01;

Sell will be True only when both the two conditions joined by the And are True on the same bar.

That is, only when both
The 6 period RSI based on closing prices falls through the level of 85
and
The Close of the previous bar was at least 1% greater than BuyPrice on that same previous bar.

You can correct part of the difficulty by using the ValueWhen so that the comparison is made between the close of the previous bar and the entry price for the trade.

That still leaves the requirement that both conditions (RSI falls through 85 and previous close showing a 1% profit) occur on exactly the same bar. This logic may be precisely what you intended, but it will not occur very often.

More likely, the logic you have in mind (pardon me for putting equations in your code) is more likely to be:
Exit the long position whenever either:
RSI falls through 85
or
the trade shows a 1% profit.

If so, try these two lines of code to cause the exit:
Sell = Cross(85,RSI(C,6));
ApplyStop(StopTypeProfit,StopModePercent,1);

This does not correspond to exactly the code you posted, but it might get you back on track.


I hope this helps.


Thanks for listening,
Howard
 
Hi Chris --

AmiBroker works on bars. Whatever bar length you can construct using your data will work. There is no adjustment necessary to go from testing using end-of-day data to testing using 5 minute bars other than setting the bar length to whatever you want. Use Automatic Analysis > Settings > General > Periodicity, and pull down the menu. The choices available will be those of the base periodicity of your database and anything longer you want to use.

If you want to use a value that is not present in the dropdown list, you can create it either using the TimeFrameSet function within your AFL code or using the Tools > Preferences > Intraday menu to create a custom intraday bar length which then show up on the menus.

Thanks,
Howard
 
There is an imbalance in parentheses.
Sell = Cross(85,RSI(C,6) AND Ref(Close,-1)>BuyPrice*1.01;
I'll assume it should be:
Sell = Cross(85,RSI(C,6)) AND Ref(Close,-1)>BuyPrice*1.01;


Hi Howard, there is one more minor error.

It must be RSIa(C, 6) if he wants to set different array (O, H, L, C or Avg). RSI(C, 6) has too many arguments because that RSI function always uses Close. Of course you know all of that so it's not my intention to teach you. ;-) Just correcting it for ch90.
 
Part of the problem you are having is that BuyPrice is set to some value for every bar. Unless you explicitly refer to the value that BuyPrice had on the bar of entry, the value returned by a reference to BuyPrice will probably not be the value you want. To specify the specific BuyPrice, use a construction such as:
EntryPrice = ValueWhen(Buy,BuyPrice);

Hi Howard,

That code won't necessarily give the correct entry price either, which is something I've been finding in my system testing. I haven't found any solution to this problem. Using the OP's code as an example, he's buying when the RSI crosses above 15 and sells when RSI crosses below 85. Now suppose the stock crosses above RSI 15, then crosses back below RSI 15, then crosses back above RSI 15 again, all before any sell signal is given. This will produce 2 buy signals. The system test will take the first of the 2 buy signals, but the code you gave above will give the price when the most recent buy signal was given, ie. the 2nd buy signal. Is there any way around this?

I'm trying to do a different, but related thing in my code. I'm trying to get the number of bars since the stock actually bought. BarsSince(Buy) doesn't always produce the correct answer, due to there often being several buy signals before a sell signal is given. How can I get the correct value?

thanks
 
A "latch" maybe the answer
I use one for some of my codes
I only have it constructed in Metastock Code but
Anyone with ami skill should be able to work it out

What it does is lock in the FIRST occurrence and ignores all others until
Exit.
 
Hi AlterEgo --

There are several ways to treat multiple signals. I did not address any of the options in my earlier posting.

If you have two (or more) Buys before one Sell, and you want to ignore the subsequent Buys, one way is through use of the SetBacktestMode statement. From the manual:

------------
SETBACKTESTMODE
- Sets working mode of the backtester Trading system toolbox
(AFL 3.0)


SYNTAX SetBacktestMode( mode )
RETURNS NOTHING
FUNCTION Sets working mode of the backtester. A 'mode' parameter is one of the following backtest modes:
Supported backtest modes:

backtestRegular - regular, signal-based backtest, redundant signals are removed as shown in this picture
backtestRegularRaw - signal-based backtest, redundant (raw) signals are NOT removed, only one position per symbol allowed
backtestRegularRawMulti - signal-based backtest, redundant (raw) signals are NOT removed, MULTIPLE positions per symbol will be open if BUY/SHORT signal is "true" for more than one bar and there are free funds, Sell/Cover exit all open positions on given symbol, Scale-In/Out work on all open positions of given symbol at once.
backtestRotational - rotational trading system see this.

EXAMPLE // default, as in 4.90, regular, signal-based backtest, redundant signals are removed
SetBacktestMode( backtestRegular );

// signal-based backtest, redundant (raw) signals are NOT removed, only one position per symbol allowed
SetBacktestMode( backtestRegularRaw );

// signal-based backtest, redundant (raw) signals are NOT removed,
// MULTIPLE positions per symbol will be open if BUY/SHORT signal is "true" for more than one bar and there are free funds
// Sell/Cover exit all open positions on given symbol, Scale-In/Out work on all open positions of given symbol at once.
SetBacktestMode( backtestRegularRawMulti );

// rotational trading mode - equivalent of EnableRotationalTrading() call
SetBacktestMode( backtestRotational );


-------------

Another is through use of the Exrem statement.
Again from the user manual:
-----------
EXREM
- remove excessive signals Trading system toolbox
(AFL 1.5)


SYNTAX exrem( ARRAY1, ARRAY2 )
RETURNS ARRAY
FUNCTION removes excessive signals:
returns 1 on the first occurence of "true" signal in Array1
then returns 0 until Array2 is true even if there are "true" signals in Array1
EXAMPLE buy = ExRem( buy, sell );
sell = ExRem( sell, buy );

-------------

Another is to use looping code to handle the conditions exactly as you wish.

Thanks,
Howard
 
Hi Howard,

That code won't necessarily give the correct entry price either, which is something I've been finding in my system testing. I haven't found any solution to this problem. Using the OP's code as an example, he's buying when the RSI crosses above 15 and sells when RSI crosses below 85. Now suppose the stock crosses above RSI 15, then crosses back below RSI 15, then crosses back above RSI 15 again, all before any sell signal is given. This will produce 2 buy signals. The system test will take the first of the 2 buy signals, but the code you gave above will give the price when the most recent buy signal was given, ie. the 2nd buy signal. Is there any way around this?

I'm trying to do a different, but related thing in my code. I'm trying to get the number of bars since the stock actually bought. BarsSince(Buy) doesn't always produce the correct answer, due to there often being several buy signals before a sell signal is given. How can I get the correct value?

thanks

Alterego, add this to your code, below the buy and sell lines.

buy = ExRem( buy, sell );
sell = ExRem( sell, buy );
 
I knew this was going to be fun ;)

First let me thanks all of you for the time you took in replying to my newbie questions but also for going way beyond that. Kudos!

I've read everything twice and the code I am going to further test will be something like:

SetBacktestMode( backtestRegularRaw ); // should be the same as buy = ExRem( buy, sell ); and sell = ExRem( sell, buy ); if I've it right

BuyPrice = Open; //this is defined in the backtester setting windows
SellPrice = Open; //this is defined in the backtester setting windows
TradeDelay = 0; //this is defined in the backtester setting windows
vPrice = (High+Low)/2;
vPeriod = 6;
vAbove = 15;
vBelow = 85;

Buy = Cross(RSIa(vPrice,vPeriod),vAbove );
Sell = Cross(vBelow,RSIa(vPrice,vPeriod)) AND Ref(Close,-1)>BuyPrice*1.01; // indeed I want both conditions to be met if I am looking for a minimum profit of 1%

I am sure I'll additional questions later on. A good week to all. :)
 
Hi Chris --

Be very careful. The system you defined has a "future leak". It uses data that is not available at the time the signal is generated. The backtest results will appear to be fantastic, but the system cannot be used in real time.

The combination of
BuyPrice = Open;
SetTradeDelays(0,0,0,0);

tells AmiBroker to buy at the open of the action bar at the opening price.

VPrice = (H+L)/2;
uses data that is not known until later in the bar. This is the future leak.

Intentionally using future data can be very useful -- for example in studying profit potential. Some trading system development platforms attempt to prohibit use of future data. AmiBroker allows it and gives you tools to effectively use it.

Thanks,
Howard
 
SETBACKTESTMODE
- Sets working mode of the backtester Trading system toolbox
(AFL 3.0)

Hi Howard,

Are you suggesting using backtestRegular? This makes no difference in my code. I understand this is the default setting anyway. This removes additional trade signals from the trades results, but not from the Buy Array itself as far as I can see. ie. Buy still equals 1 on the additional days. I've plotted "Buy" as an indicator to show this.

EXREM
- remove excessive signals Trading system toolbox
(AFL 1.5)

SYNTAX exrem( ARRAY1, ARRAY2 )
RETURNS ARRAY
FUNCTION removes excessive signals:
returns 1 on the first occurence of "true" signal in Array1
then returns 0 until Array2 is true even if there are "true" signals in Array1
EXAMPLE buy = ExRem( buy, sell );
sell = ExRem( sell, buy );

I tried that, but unfortunately I need to place the ExRem( buy, sell ) statement before the Sell line in the code, as I am using BarsSince(Buy) to calculate the Sell. And if I place it before the Sell line, I get an error "Variable 'sell' used without having been initialized".
 
Dear Howard,

What I intended to do is once my conditions for buying and selling are met to respectively buy and sell at the open of the next bar.

Would it work to define vPrice as = Ref((High+Low)/2,-1)?
or maybe even more elegant SetTradeDelays(1,1,1,1)?

Oh and I had another question, at the end of the backtest does it closes automatically all open positions? It seems so.

Cheers, Chris
 
Dear Howard,

What I intended to do is once my conditions for buying and selling are met to respectively buy and sell at the open of the next bar.

Would it work to define vPrice as = Ref((High+Low)/2,-1)?
or maybe even more elegant SetTradeDelays(1,1,1,1)?

Oh and I had another question, at the end of the backtest does it closes automatically all open positions? It seems so.

Cheers, Chris

Hi Chris --

Either using the Ref(xxx,-1) or setting delay to 1 will work for the backtest. Which you use will depend in part on how you plan to generate signals when you move from development to trading. By that I mean when you will load the data necessary to generate a signal into AmiBroker and when you will place your trading order. You can get an idea of what happens and at what time by using the BarReplay feature. AmiBroker does not know that a bar has closed until the next bar has begun (neither does any other trading system development platform know that). But AmiBroker does treat every new data point as though it is the last one that will be recorded for that bar. That means you can download snapshot or real-time data near the close of trading and the price at that time will be interpreted as being the Close for that bar. If that price is the high or low so far for the bar, it will also update the value of High or Low as well.

Backtest does mark-to-market all open trades at the end of the backtest period and include those results as if the trade was closed. The trade list for a backtest will show any trades that are open at the end of the period as being open.

Thanks,
Howard
 
Hi Howard,

To better explain what I'm trying to do in Amibroker, see the below code. Note, this is only an example of what I'm trying to code, not the exact code that I'm using. "DaysInTrade" gives an incorrect value when there are 2 or more buy signals before a sell signal. Placing ExRem(Buy,Sell) at the end of the code doesn't change this, only removes the extra trade signals from the chart - "DaysInTrade" plotted still shows it re-setting back to 1 on the additional (now invisible) Buy signal days. And placing the ExRem(Buy,Sell) code before the "DaysInTrade" line produces an error, as "Sell" hasn't been initialized. And setting SetBacktestMode to regular also doesn't change this behaviour. So how can I incorporate lopping code, or other method, into this code so that it works correctly?


SetTradeDelays (0,0,0,0);

Buy = Cross(30,RSI(5));

BuyPrice = C;

DaysInTrade = BarsSince(Buy) + 1;

Plot(DaysInTrade,"DaysInTrade",colorRed);

SellTarget = LLV(C,DaysInTrade) + 2*ATR(20);

Sell = Cross(C,SellTarget);

SellPrice = C;


thanks for your help

regards,
AlterEgo
 
Hi Howard,

Are you suggesting using backtestRegular? This makes no difference in my code. I understand this is the default setting anyway. This removes additional trade signals from the trades results, but not from the Buy Array itself as far as I can see. ie. Buy still equals 1 on the additional days. I've plotted "Buy" as an indicator to show this".

Hi AlterEgo,

I agree with you, I've tested the setbacktestmode; exrem and entryprice/valuewhen functions but I still have some exitprice way below my entryprice!

For example I've a buy1 at 2.11 (trade), buy2 (raw signal) at 1.92 and an sell1 (trade) at 1.86! How can I end up with a 12% loss with this system! It is probably obvious but I can't see it right now.

Here's my simple AFL for now:

//strategy formula

SetBacktestMode(backtestRegular);
SetTradeDelays(1,1,1,1); //this is defined in the backtester setting windows
//Filter=Close>10 AND Close<50; //somehow does not work ???
PositionSize=10000;
BuyPrice=Open; //this is defined in the backtester setting windows
SellPrice=Open; //this is defined in the backtester setting windows

Vmomentum1=RSIa(Close,6);

Cond11=Cross(Vmomentum1,15);
Cond12=Close>10 AND Close<50; //this works, not like filter function above

Buy=Cond11 AND Cond12;
//EntryPrice=ValueWhen(Buy,BuyPrice,1); //alternative to fix the buyprice to the entryprice of the trade

Cond21=Cross(85,Vmomentum1);
Cond22=Close>BuyPrice*1.01; //do not sell if profit below 1%
//Cond22=Close>EntryPrice*1.01;

Sell=Cond21 AND Cond22;

//Buy=ExRem(Buy,Sell); // seems to be the same as SetBacktestMode(backtestRegular);
//Sell=ExRem(Sell,Buy); // seems to be the same as SetBacktestMode(backtestRegular);

Hope I am not boring you with my newbie struggles :)
 
ch90 have you tried testing it on one company and setting the filter =1 to go through every day to see what values are for the indicators you are using
 
dear tminus, I am not sure how to do it but it would be interesting to see wich values are stored in the array. I'll continue going through the manual and this good forum to find a logical explanation to this major mystery. thank you. chris
 
dear tminus, I am not sure how to do it but it would be interesting to see wich values are stored in the array.

Try plotting the array as an indicator, like in the plot statement in my code above. Then when you have the formula editor open with your code, just click on the "apply indicator" icon.
 
Thanks mate, I've explored the signals and indeed with a code such as

EntryPrice=ValueWhen(Buy,BuyPrice,1);

the entryprice is the latest buyprice of the raw signal and what I would like is to recall the buyprice of the trade (open position) in order to exit only with a minimum profit of e.g. 1%. It is not a stop loss since the exit has 2 conditions:

Sell = Cross(vBelow,RSIa(vPrice,vPeriod)) AND Ref(Close,-1)>EntryPrice*1.01;

I've spent hours reading the manual and this forum and I hope someone will be able to point me in the right coding :banghead:
 
Top