You are on page 1of 12

eSignal Formula Script (EFS) Tutorial Series

BACK TESTING TUTORIAL 3


Position Management

Summary: This tutorial will explore a detailed back testing example that manages positions with
multiple closing trades. This formula example accomplishes this type of strategy logic by utilizing
the lot size parameter of the Strategy methods. This formula example also combines the trailing
stop and profit target logic explained in Back Testing Tutorial 2 to illustrate a back testing formula
that contains some commonly used elements of a trading system. The preMain() code will not
be covered in detail in this tutorial. However, the completed formula will include the preMain()
code for review. There is a link for downloading the completed formula at the end of the tutorial.
All of the formula examples in the tutorial series are for educational purposes only. They are not
designed for profitability and are not intended for trading. The strategy rules used for these
examples have been simplified for educational purposes.

Topic List:
1. Position Management Formula Example:
BtDonchian_PositionManagement.efs

3.1 Position Management Formula Example:


BtDonchian_PositionManagement.efs
The following example is a basic strategy that goes long when a new 10-period Donchian
high occurs and goes short when a new 10-period Donchian low occurs. The exit strategy
for this sample uses the Average True Range (ATR) study for the basis of the trailing stop
logic. The initial stop is set to the Middle Donchian channel. The trailing stop will
increment in the direction of the position by 25% of the previous bar's 10-period ATR. To
complete the position management for this strategy, the positions will also be partially
(50%) closed at a fixed profit target. This target will initially be set to 1.5 times the
difference between the entry price and the initial stop price, which creates a minimum 1.5
reward risk ratio for the first half of the position. The second half of the position will be
closed by the trailing stop. Positions will not be closed by reversing trade signals.
3.1.1 Setup Indicators
This strategy will be using 4 indicators in total, Average True Range, Upper, Middle and
Lower Donchian. Therefore, a global variable will be created for each series along with a
global initialization flag.

A condition at the top of main() is added to prevent the formula code from executing in
real time as this formula is for back testing only. This is done (line 48) by checking for a
bar index of 0 with getCurrentBarIndex(). The global variables for the indicator series
are initialized in the bInit initialization routine followed by the final return statement. This
initialization routine is used because the global series variables only need to be initialized
one time. Setting bInit to true at the end of this routine prevents this code block from
executing again due to the bInit conditional if() statement (line 50). The return statement
up to this point is only returning the current value of the Upper and Lower Donchian
indicators to be plotted on the chart. The ATR and Middle Donchian indicators will not be
plotted on the chart, but will be used for the Strategy conditions that follow.

3.1.2 Add Entry Conditions


The next step is to add the entry conditions, which are the same entry conditions used in
the examples for Back Testing Tutorial 2. In review, the entry conditions will compare the
price bar data to the Upper and Lower Donchian channels to trigger the entry signals.
Two local variables will be declared to refer to the previous bars values of the Donchian
channels, nU and nL. The conditions for this strategy will be evaluated on the bar that
just closed so the two variables will be set to the values of the Donchian series from bar 1. After assigning the values to these variables, a null check is performed to validate the
data stored in those variables. If they are found to be null, the formula will be exited with a
return statement. This will occur at the beginning of the chart data where the Donchian
channels have not been established yet due to the minimum number of bars required to
produce valid data for these indicators. This code is added just after the initialization
routine for the series objects, xUpper and xLower.

As discussed in Back Test Tutorial 2, most back testing formulas will require some
specific code logic to prevent multiple trades from occurring on the same bar. For this
strategy, since the exit rules do not allow for positions to be closed with reversing entry
signals, the entry conditions can be placed in an else statement associated with the initial
if() statement for the exit conditions. The initial exit condition checks to see if the strategy
is currently holding a position before evaluating the exit strategy. If that condition is false
then the else portion of this condition will be executed, which contains the following entry
conditions. This if() else structure ensures that a new position will not be entered on
the same bar that closes a position.

The two entry conditions on lines 87 and 91 simply compare the high and low of the
current bar to the previous bars Upper and Lower Donchian channels. If one of these

conditions evaluates to true, a long or short position will be reported to the Strategy
Analyzer, respectively. Make note of the nEntry variable on lines 88 and 92. This
variable stores the entry price that will be used for the entry trade, which must be
validated to ensure that a realistic entry price is recorded. In the case of the long entry
condition, it would be possible for the open of the bar to be higher than the previous bars
Upper Donchian value, nU. Therefore, the open of the current bar would be the most
realistic entry price as that would have been the trade that triggered the entry signal. If
the current bar opened below the previous bars Upper Donchian channel, then it can be
assumed that at some point during the current bar the price moved higher, which then
triggered the long entry signal. In this case, nU would be the realistic entry price for the
entry trade. Passing nU and the open price to the Math.max() function will set nEntry to
the maximum value between those two values. Therefore, the open will only become the
recorded entry price if the open is greater than nU. The short condition would evaluate
the reverse logic of the long condition and use Math.min() to record the proper entry
price, which would be the lower value between nL and the open price.
3.1.3 Add Exit Conditions
Before diving into the specific code for the exit conditions, there are a few required
elements that need to be added, which will be used within these conditions. First, the
logic for the trailing stop will be added. A global variable is needed to store the stop price,
which will be named, nStop.

When a new position is taken, the initial stop is set to the previous bars Middle Donchian
indicator. This series was already declared in the fist section. All that needs to be added
now is the local variable, nM, which will be assigned to the previous bars value for this
series. The nM variable will also be added to the null check routine for validation.

Now the initial stop value can be assigned to nStop, which occurs inside the entry
conditions (lines 129 and 134 below).

The next step for the stop logic is to add the code that increments this price, which creates
the trailing stop. For each bar where an active position does not get stopped out, the stop
will be incremented by 25% of the previous bars ATR. Another local variable, nInc, will
be added to store this value, which is also added to the null check routine.

Just after the null check routine and above the exit strategy is where this logic needs to be
placed. This trailing stop adjustment needs to be done before evaluating the stop
conditions for the current bar to simulate the real time adjustment after the completion of a
bar, which did not close the current position. Likewise, if the position was closed, the
nStop variable needs to be reset to null at this instance as well.

As the formula process the new bar, the strategy first checks to see if there is an active
position (line 68). If line 68 is true, nStop is reset to null. If line 68 is false, then the else
block is executed and nStop gets incremented accordingly.

The last modification to make for the stop logic is to add the nStop variable to the return
statement in main() so that it will be plotted on the chart for visual reference. When the
value is null, there will not be a line plotted on the chart. This appearance on the chart is
controlled by the plot type assigned to this returned series in preMain(), which is
PLOTTYPE_FLATLINES.

With the stop logic in place, now the logic for the profit targets will be added. The profit
target will be a fixed target. However, when the price breaches this target only half of the
position will be closed. The remaining half will persist until it is taken out by the trailing
stop. To process this logic, there will be two new global variables needed. nTarget will
be used to store the fixed profit target level and bTargetBreach will be used to keep track
of when, or if, the target is hit.

bTargetBreach is a Boolean variable that is initially set to false. A false value will indicate
that the profit target has not been breached for the current position. When the target is hit
and half of the position is closed, bTargetBreach will be set to true. This variable will be
used in the profit target conditions to ensure that this exit trade will only occur once and
allow the other half to be closed by the trailing stop.
Following the same logic used for nStop, these two profit target variables will also need to
be reset after a position is closed, which can be done within the same condition that
resets nStop (line 72 and 73 below).

Also, in similar fashion to the stop logic, the profit target will be plotted on the chart for
visual reference, which is why it also is reset to null when there is no active position. It will
be added to the return statement and will also be formatted with the plot type of
PLOTTYPE_FLATLINES in preMain().

At this point, all of the variables needed for coding the exit conditions are in place. As
mentioned in the previous section, the initial condition for the exit strategy will check for an
active position. If that condition is true, then the exit rules will be evaluated. The next
layer of conditions that will be evaluated will simply check to see if the position is long or
short. Both conditions cant be true at the same time because the Strategy object does
not allow this by default. Therefore, they can be evaluated in succession without an else
if() statement.
Heres the complete code for the long exit strategy, which includes the logic for both profit
target and trailing stop conditions. The conditions for the short exit strategy will
immediately follow starting at line 105.

If the strategy is long at line 85, then the long exit rules will be evaluated. First, make note
that each of the possible exit rules are evaluated separately and with an if() else if()
structure right down to the last possible exit condition. This is done to prevent multiple
long exit conditions from being executed on the same bar. If each exit condition were
placed in their own if() statement, each condition would be evaluated in succession, which
would allow for the possibility of multiple .doSell() executions. This would lead to
improper back test results based on the intended rules for this strategy. Remember that it
is the specific code logic of the formula that determines what trades, fill price and number

of shares are reported to the Strategy Analyzer. The Strategy Analyzer and the EFS
Strategy object do not have the ability to make any logic decisions.
In reviewing the specific code logic in the image above, a breach of the trailing stop,
nStop, is evaluated first starting at line 86. If the bar opened below the stop level, the
position will be closed at the open, which is the result of the fill type and fill bar constants
used in the .doSell() method on line 87. If the open did not breach the stop level, then the
low of the bar is compared to the stop level, as the price could have moved lower during
the interval and triggered the stop trade. This condition is on line 89, which records the
trade price at the stop level, nStop.
One observation that should be made at this point is the choice of code logic that was
used to execute the stop trades. It could have been done in a similar manner to the entry
logic where a local variable, such as nExit, could have been assigned to the minimum
value between nStop and the open of the bar. Then there would have been only one
conditional statement needed that would compare the low of the bar to nStop and then
pass nExit as the stop price to .doSell() with a fill type of Strategy.STOP. This would
have eliminated the need for two different .doSell() calls where one uses
Strategy.MARKET and the other uses Strategy.STOP. This would actually be a slightly
better way to code it. There are two reasons why the current code logic was used. First,
to illustrate that there can be more than one way to properly interpret the intended rules of
a strategy. Most importantly, this logic illustrates an example of when a strategy method,
such as .doSell(), can be used with a fill type of Strategy.MARKET and a fill bar of
Strategy.THISBAR. Remember from Back Test Tutorial 1 that this combination of
constants records the trade price at the open of the current bar that is being processed.
This combination of constants often creates confusion and misunderstanding for new
users. Many new users first back testing formulas are coded to evaluate bar 0, or the
current bar, to trigger trade signals. This type of logic often compares the close of bar 0 to
an indicator to trigger a trade. In this case, using the MARKET/THISBAR combination of
constants can falsely inflate the back test results because the open of the bar is usually a
better price than the close of the bar that was used to evaluate the trade signal. The rule
of thumb to remember is that if the strategy logic evaluates trade signals based on bar -1,
like the example in this tutorial, then recording a trade at the open of bar 0 with
MARKET/THISBAR is acceptable logic. If the strategy logic only evaluates bar 0 for trade
signals, the MARKET/THISBAR combination of constants is not acceptable logic for the
reasons just explained. An alternative solution to avoid any confusion related to the
Strategy.MARKET fill type is to simply not use it. Instead, use Strategy.LIMIT or
Strategy.STOP for all of the strategy trades and pass in the specific trade price that is
validated by the code logic. This concept also applies to the code logic used for the profit
target, which is discussed next.
When neither of the trailing stop conditions is true, then the profit target conditions will be
evaluated. The first profit target condition on line 92 checks to see if the profit target had
previously been executed for the current position. If not, then the current bar is evaluated
for a breach of the profit target with the open and high of the bar. If the profit target has
been breached, then half of the position is closed at the appropriate price level. When this
event occurs, the bTargetBreach variable is set to true so that the profit target conditions
will not be evaluated again for the current position. Determining the number of shares (or
contracts) to close is done on line 93. The local variable, nLot, is declared and assigned
to half of the current position by retrieving this number using the
8

Strategy.getPositionSize() method and dividing by 2. Notice also that this calculation is


rounded to the nearest whole number with the Math.round() function. The lot size
parameter of the strategy methods only accepts whole numbers. Passing the nLot
variable to the lot size parameter of the .doSell() calls records an exit trade that closes
only half of the current position.
Make note that for all of the .doSell() calls, the lot size parameter is being specified for
both the trailing stop and profit target conditions. It is very important to remember to
specify the lot size parameter when closing trades in back testing formulas that will be
utilizing the lot size parameter to manage positions. Because the lot size parameter is an
optional parameter, the number of shares that will be recorded for a trade if it is not
specified will be the Default Lot Size number that is entered into the Back Testing dialog.
In the case of this example, notice that Strategy.ALL is passed to the lot size parameter
for the .doSell() calls in the trailing stop conditions. Using Strategy.ALL ensures that the
current total number of shares held will be closed, which could be a different number than
the default number specified in the Back Testing dialog. For example, if the profit target
was executed and the default lot size entered in the Back Test dialog was 100, then there
would be 50 shares left to sell when the trailing stop condition is executed. Without
specifying Strategy.ALL for the lot size, the .doSell() method would try to sell 100, which
will create erroneous back test results.
To complete the exit strategy, the conditions for closing short positions is added, which
follows the same logic as the long conditions.

However, there is one minor difference on line 113 that sets the value for the nLot
variable. The .getPositionSize() method returns a negative number if the position is short
and positive if long. The lot size parameter of the strategy methods also requires positive
numbers. Therefore, the rounded result also needs to be passed to the Math.abs()
function, which converts the number to an absolute value.

3.1.4 Add Visuals


Once again, the last step in the development process is not required, but very helpful
when testing and verifying the results of a back testing formula in the Strategy Analyzer.
Having a visual representation of the trading activity on the chart helps to identify the bars
where trades occurred, which saves time during the process of finding the corresponding
bar in the chart that is reported in the Strategy Analyzer.
For this example, the background color for the bars will be colored dark green or maroon
when the strategy is long or short, respectively. This code is added near the bottom of
main() just before the return statement that is plotting the indicators.

On the chart, the first bar that is colored dark green (or maroon) will indicate the bar where
the entry trade was executed. The date and time of these bars will also be found in the
list of trades on the Trades tab in the Strategy Analyzer.

There are many different options available to highlight the trading activity or position
status on the chart. For example, an image, shape or text could be drawn above or below
10

the bar using one of the drawing functions, drawImageRelative(), drawShapeRelative(),


or drawTextRelative(). These would be added on the line just before or after the
.doLong() and .doShort() calls. The price bars could also be colored rather than the
background of the bars (see setColorPriceBars() ). These methods are the most
common, but certainly not the only possibilities that could be created.
3.1.5 Execute Strategy Analyzer
Click on the following link to download a completed copy of this formula,
BtDonchian_PositionManagement.efs.
Trade 139 in the image below is the trailing stop trade for the long position that was
closed on 9/6 (bar -16) from the chart image above.

The trade was recorded at the stop price of 80.26 for 50 shares. The default number of
shares used for this back test was 100. The first 50 shares for this long position were sold
at the profit target level of 79.43, which occurred 13 bars before the stop on 8/17. This
can easily be identified on the chart by looking for the khaki horizontal line (the profit
target) that starts where this long position was first taken.
This back test didnt include any slippage or commission. However, once the logic for a
back testing formula has been verified to be accurate and realistic, it is recommended to
add some slippage and commission to the back tests to add an additional layer of realism
to the final results.

Whats Next?
This concludes the Back Testing tutorials for beginners. Now its time to start coding
some of your trading system ideas and back test them with the Strategy Analyzer. Any
11

questions or challenges that arise should be posted to the EFS Back Testing forum at
www.esignalcentral.com. Also remember to post your formula code along with your
detailed questions.

12

You might also like