Sunteți pe pagina 1din 8

http://www.adaptrade.

com/BreakoutFutures/Newsletters/Newsletter010
3.htm
The Breakout Bulletin
The following article was originally published in the January 2003 issue of The Breakout
Bulletin.

EasyLanguage Techniques
Many of you are TradeStation users and have no doubt used (or tried to use)
EasyLanguage to program your own trading system. The name notwithstanding,
EasyLanguage can be tricky, even for experienced programmers. So, this
month, I'm going to cover a few EasyLanguage techniques that may be
nonobvious to the uninitiated. I'll first discuss a few basic concepts that are
necessary to understand in order to successfully program a system in
EasyLanguage. Then I'll address a couple of commonly encountered problems
that arise when programming a system. Finally, I'll show you some
EasyLangauge code that accumulates an equity curve using fixed fractional
position sizing. This will illustrate the use of functions.
Some Basics
I don't have the time or space in this format to cover the basics of
EasyLanguage programming or language syntax in any comprehensive way, but
I would like to discuss the concept of program flow and execution. If you've
programmed before in other languages, EasyLanguage probably looks similar to
other procedural languages, such as BASIC, C, Pascal, or Fortran. With respect
to syntax, EasyLanguage is similar in many ways to these other languages.
However, it differs in terms of how the program is executed. Traditional
programming languages, such as the ones mentioned, execute from top to
bottom, then stop at the end. EasyLanguage code, on the other hands, is
executed on each bar of the chart to which it's applied. Confusion can
sometimes arise because variables retain their values from bar to bar. This is
true even within functions. However, the declarations at the top of an
EasyLanguage program are executed only once, on the first bar.
Consider the following code for a make-believe system (or "strategy" in the
latest jargon of TradeStation/EasyLanguage):
Input:

Len1
Len2

(10),
(5);

Var:

A1
B1
C1

(0),
(0),
(0);

A1 = C1 + 1;
B1 = A1 + 1;
C1 = B1 + 1;
{ Comments are written between braces: the following
2 lines generate the entry orders }
If C1 > 10 then Begin
Buy next bar at C + Len1 stop;

Sell Short next bar at C - Len2 stop;


End;
The inputs to the system, Len1 and Len2, are initialized to values of 10 and 5,
respectively. You cannot change the values of inputs within the system code, so
these inputs will have the values given unless the user enters different values
from the chart.
The variables, A1, B1, and C1 are declared next. Each variable is initialized to
zero in the declaration by putting 0 in parentheses after the variable name. This
initialization happens once, on the first bar. The next three lines increment the
variables. These lines, as well as the remaining lines in the code, are executed
in order from top to bottom on each bar of the chart, and the values of A1, B1,
and C1 are "remembered" by the system from one bar to the next. On the first
bar, for example, A1 is given the value C1 + 1, which is equal to 1 because C1 is
initialized to zero on the first bar. Then, B1 gets the value A1 + 1, which is equal
to 2. Finally, C1 gets the value B1 + 1, which is equal to 3. On the second bar,
the execution begins at the line "A1 = C1 + 1." On this bar, C1 has the value of
3 from the prior bar, so A1 now gets the value C1 + 1, which is 4. B1 then
becomes 5 and C1 becomes 6. On the third bar, C1 will end up with the value 9.
On the fourth bar, C1 will become 12. On this bar, the entry orders will finally
get executed because C1 is greater than 10. (The entry condition is nonsensical,
of course, and is just to illustrate the idea of program execution.) As we'll see in
the sample function presented later, variables declared within functions also
retain their values from bar to bar.
Using conditions on one bar for entry on a later bar
Now that a few basics are out of the way, let's cover a couple of common
situations that come up in programming a system. Because the EasyLanguage
code executes on every bar, it's straightforward to define entry conditions for a
trading system that apply to the next bar. For example, if you want to buy the
next bar if today's close is less than the close of two day's ago, you would write:
If C < C[2] then
Buy next bar at market;
Buying the next bar "at market," by the way, will generate an order to buy at
the open of the next bar.
Let's make it a little more interesting by buying not at the next open but at the
highest high of the last five bars on a stop. The code for this could be written as
follows:

If C < C[2] then


Buy next bar at Highest(H, 5) stop;
However, what do you do if you want to place this order not just for the next bar
but for all subsequent bars once this condition has been met? In other words,
the setup condition is that the close is less than the close two day's ago. Once
the setup condition is met, we want to continue to look for the trade until we
either get filled or other conditions nullify the setup.
The easiest way to handle this is to define a logical variable "Setup" that is set
to "true" once the setup condition is met. Until the variable is set to false again,

the entry order is executed. For example,


Var: Setup (false);
If C < C[2] then
Setup = true;
If Setup = true then
Buy next bar at Highest(H, 5) stop;
{ Here's one way to reset the Setup variable for the next trade }
If Setup = true and MarketPosition = 1 then
Setup = false;

In this code, the variable Setup is set to "true" once the condition C < C[2] is found.
Because variables retain their values from one bar to the next, Setup will be "true"
on subsequent bars as well, so the buy order will be generated on subsequent bars
until Setup is reset to false. You always have to be careful when using this technique
to reset the Setup variables so it can be used on the next trade. There are a variety of
ways to do this, depending on the rest of your system code, but one way is shown
above. I've used the built-in function MarketPosition to detect that the long trade has
been entered. Once the trade has been entered, MarketPosition returns 1
(MarketPosition returns 0 for flat, 1 for long, and -1 for short), indicating a long
position is open. This condition is detected by the last "if" statement and used to set
Setup to false. Otherwise, Setup would remain true on all subsequent bars.
Speaking of MarketPosition...
The function MarketPosition, used above, is a very useful function. One problem,
however, is that it only references the current bar. It's sometimes useful to be able to
compare the current market position with the market position on the prior bar. For
example, this can be used to detect that a trade has exited. The common way to
reference the value of a variable or function on prior bars is to use the [] notation.
For example, C[2] is the value of the close two bars ago. If you try to write
MarketPosition[2], however, you'll get an error message. The way around this it to
define your own market position variable:
Var: MarkPos (0);
position }

{ variable for market

MarkPos = MarketPosition;
If BarNumber > 1 and MarkPos[1] = 1 and MarkPos <>
MarkPos[1] then
TrailOnL = 0;
{ long trade exited on this bar }
If BarNumber > 1 and MarkPos[1] = -1 and MarkPos <>
MarkPos[1] then
TrailOnS = 0;
{ short trade exited on this
bar }
Variables in EasyLanguage store the values on the prior bars (as far back as the
MaxBarsBack setting), so by setting our MarkPos variable equal to MarketPosition

on each bar, we are effectively storing the past values of MarketPosition. We can
then reference them using the [] notation.
Also note how the MarkPos variable is used to determine that long and short trades
have exited. To detect that a long trade has exited, we check that we were long on
the prior bar (MarkPos[1] = 1) and that our position on the current bar has changed
(MarkPos <> MarkPos[1]). The reverse logic is used to detect that a short trade has
exited. In the example above, taken from an intraday system I was testing, this logic
is used to reset flags that tell the system a trailing stop is active.
A function for fixed fractional position sizing
Of the many ways to size a trade, my favorite is fixed fractional position sizing,
where you risk a certain percentage of your account equity on each trade. For
example, you might risk 2% of your equity on each trade. The risk is usually
determined from the size of the money management stop (e.g., a 12 point stop in the
E-mini S&P represents a risk of $600) or from the largest historical loss for the
system. TradeStation includes a built-in indicator called Strategy Equity that plots
the equity curve for the system currently applied to a chart. However, if you want
the equity curve to represent fixed fractional position sizing, you're on your own in
programming your system to adjust the number of contracts for each trade according
to the fixed fractional equation. The function shown below will do this for you. It
returns the number of contracts for the next trade based on the account equity, trade
risk, and fixed fraction. It accumulates the equity from trade to trade.
Here's the code, preceded by a lengthy comment:
{ User function: NConFF
Calculate the number of contracts for the current trade assuming
a fixed percentage
of account is risked on each trade.
This function implements a fixed fractional approach to position
sizing; see R. Vince, Portfolio
Management Formulas, 1990 for a complete discussion of fixed
fractional trading. The function is
intended to be called by a trading system prior to each trade to
determine the number of contracts
to trade based on the accumulated trading equity, the risk of the
current trade, and the amount to
risk on each trade.
INPUTS:
StEqty: initial account size (starting equity) in dollars.
RiskPer: percentage risk per trade. This is the so-called "fixed
fraction" of fixed
fractional trading.
TrRisk: risk for current trade in dollars; should be positive.
This number can be different
for each trade if desired.
MinNEq1: Set MinNEq1 to either 1 or 0. If equal to 1, the number
of contracts is always
at least equal to 1. In other words, if the number of contracts
would otherwise be equal
to 0 because of a small account size or high trade risk, this

sets the number of contracts


equal to 1.
OUTPUT:
The function returns the number of contracts for the current
trade. If the account equity falls below
zero, the number zero ("0") will be returned. This is true even
if MinNEq1=1.
You can use the "Expert Commentary" tool in charting to see the
account equity, current profits,
trade risk, risk percentage, and number of contracts for the next
trade on each bar.
NOTES:
1. This version writes error messasges to the TradeStation
MessageLog, so it will only work in
TradeStation versions 2000i and TS 6. For TradeStation 4.0,
change "MessageLog" to "Print."

Michael R. Bryant
Breakout Futures
www.BreakoutFutures.com
mrb@BreakoutFutures.com
11/28/00
Copyright 2000
}
Input: StEqty
RiskPer
TrRisk
MinNEq1
Var:

Breakout Futures
(NumericSimple),
(NumericSimple),
(NumericSeries),
(NumericSimple);

NCon
(0),
Equity (0);

{
{
{
{

starting account size, $ }


risk percentage }
risk for current trade }
=1 --> # contracts at least 1 }

{ number of contracts }
{ account equity }

Equity = StEqty + NetProfit + OpenPositionProfit;


If ABSVALUE(TrRisk) > 0 then
NCon = IntPortion(RiskPer/100 * Equity/ABSVALUE(TrRisk))
else Begin
MessageLog("**Error: Trade risk <= 0. Assign positive value to
TrRisk.");
NCon = 0;
end;
If MinNEq1 > 0 and NCon < 1 then
NCon = 1;
If Equity <= 0 then Begin
MessageLog("**Warning: Account equity <= 0.");
NCon = 0;
end;
#BeginCmtry
If CheckCommentary then Begin
Commentary("Starting Equity: $", StEqty:0:2, NewLine);
Commentary("Net Profit (closed + open): $", (NetProfit +
OpenPositionProfit):0:2, NewLine);
Commentary("Current Equity: $", Equity:0:2, NewLine);
Commentary("Trade Risk: $", TrRisk:0:2, NewLine);
Commentary("Risk Percentage: ", RiskPer:0:2,"%", NewLine);

Commentary("Number of Contracts for Next Trade: ", NCon:0:0);


end;
#End;
NConFF = NCon;

This example provides a good opportunity to explain EasyLanguage functions. First,


why would you choose to use a function? A function is generally used to contain
code that will be used repeatedly. Instead of rewriting the code every time you need
it, you write it into a function and call the function whenever you need that code.
The NConFF function above, for example, can be called from any trading system. If
the code were written into the system, it would not only result in more complicatedlooking system code, but we'd need to duplicate the effort the next time we wanted
that functionality in another system.
Notice how the inputs to the function are declared by "type." In this case, they're
declared "NumericSimple" to indicate that they represent simple numbers (as
opposed to arrays of numbers or logical variables, for instance). This is different
from in a strategy, where the inputs are initialized in the declaration. Another
important feature of functions is that they return a value. The NConFF function
returns the number of contracts, given by variable NCon. The function must contain
a line in which the function name is assigned the return value. In this example, the
last line in the function -- NConFF = NCon -- serves this purpose.
The NConFF function is basically a two-line function. Everything else is basically
bells and whistles. The first key line is the first statement of the function, where it
calculates the total account equity as the sum of the starting account equity, current
open profit, and total closed profit. The second key line is two lines below, where it
calculates the number of contracts, NCon, using the fixed fractional formula. This
line divides the account equity by the trade risk and multiplies by the fixed fraction.
Notice that the function first checks to make sure the risk is not zero. This is done to
insure that the function doesn't accidently divide by zero, which would generate an
error. If the trade risk, TrRisk, is zero, a message is written to the Message Log, and
the number of contracts is set to zero.
The next "if" statement checks the input MinNEq1, which enables the user to force
the number of contracts to 1 in cases where it would otherwise be equal to zero due
to insufficient equity. Following that, the function checks to see if the account equity
is still positive. If not, a message is output to the Message Log, and the number of
contracts is set to zero so that the next trade will be skipped. Lastly, I've used the
Commentary feature of EasyLanguage to write out some information. Commentary
statements write out information to the Analysis Commentary window ("Expert"
Commentary in TS 2000i), which is accessed through the Analysis Commentary tool
in the Drawing menu in a chart window. When you click the Analysis Commentary
tool on a bar on the chart, the Analysis Commentary window pops up, revealing any
commentary that's available on that bar. The NConFF function makes all the
calculated results available on each bar in this manner. For example, if the system
running on your chart calls this function, you can see the net profit, current equity,
and the number of contracts for the next trade by clicking the Analysis Commentary

tool on any bar.


To illustrate how you would use this function in a system, consider the following
simple system:
Input: StEqty
StopSz
points }
RiskPer
MinNEq1
}
Var:

TrRisk
NCon

(30000), { starting account size, $ }


(15),
{ money management stop size,
(3.0),
(1);

{ risk percentage }
{ =1 --> # contracts at least 1

(0),
(0);

{ risk for current trade }


{ number of contracts }

TrRisk = StopSz * BigPointValue;


NCon = NConFF(StEqty, RiskPer, TrRisk, MinNEq1);
{ Here's where you call the function }
If C < C[2] then
Buy NCon contracts next bar at Highest(H, 5) stop;
If MarketPosition = 1 then
Sell next bar at EntryPrice - StopSz stop;
A couple of notes:
1. The trade risk is calculated from the size in points of the money management
stop using the built-in function BigPointValue, which gives the dollar value
of a full point move in the contract. For example, for the e-mini S&P,
BigPointValue = 50.
2. The call to the function is the second statement, NCon = NConFF(...). The
function returns the number of contracts, which is assigned to the variable
NCon. We then use NCon in the buy order: Buy NCon contracts ...
3. The sell statement implements the money management stop, which is a stop
order StopSz points below the entry, given by the built-in function
EntryPrice. Since we haven't specified the number of contracts for the sell
order, it will exit the entire position.
4. This code is for TS 6. For earlier versions of TradeStation, the sell statement
should be "ExitLong."
Hopefully, this function example, and the other examples, help address a few of
the more common problems traders have in writing trading systems in
EasyLanguage. Of course, there are a myriad of issues in using EasyLanguage
to develop trading systems, but I think that's enough for now.

Good luck with your trading.

Mike Bryant
Breakout Futures

S-ar putea să vă placă și