Newsletter
Article Library
Videos
What's New
About Us
Site Map
Search

 

Unlimited Systems

Auto-generate unique

trading strategies for

TradeStation.

 

Position Sizing Tool

Position sizing software for

trading. Trade smarter.

Maximize results.

 

 

 

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   (10),

        Len2   (5);

 

Var:    A1     (0),

        B1     (0),

        C1     (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);           { variable for market position }

 

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 Breakout Futures
 }
 Input: StEqty   (NumericSimple), { starting account size, $ }

        RiskPer  (NumericSimple), { risk percentage }

        TrRisk   (NumericSeries), { risk for current trade }

        MinNEq1  (NumericSimple); { =1 --> # contracts at least 1 }
  
 Var:   NCon   (0),  { number of contracts }

        Equity (0);  { 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 complicated-looking 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   (30000), { starting account size, $ }

       StopSz   (15),    { money management stop size, points }

       RiskPer  (3.0),   { risk percentage }

       MinNEq1  (1);     { =1 --> # contracts at least 1 }
  
 Var:  TrRisk   (0),     { risk for current trade }

       NCon     (0);     { 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