Guide to Developing eSignal Strategies
Developing advanced trading strategies within eSignal is relatively simple. Trading system developers simply need to learn a few basic functions and processes to develop a system to back-test and trade. eSignal provides all of the functions for entering, exiting, trailing stops and establishing profit targets. All the developer needs to learn is where and when to properly use the available functions.
In these examples, we will go through the basics of developing strategies within eSignal. Well start by developing a simple ENTRY/EXIT strategy, then move into more complex code types like time restricted entry/exit systems and stop trailing systems.
The example code within this document is for example purposes. Neither Matheny Enterprises nor eSignal make any implied guarantees of the accuracy or profitable nature of any example code contained herein.
Strategy System Basics
The eSignal strategy back-testing system steps through the chart (from oldest bar to newest bar or left to right) and executes your code. The code (or eSignal Formula Script EFS) is a set of instructions that are executed in order (top to bottom).
Within the EFS code, developers control variables, conditions and execute strategy trading orders (for back-testing purposes only) with a series of simple commands. The structure of these commands and of the order of the commands is critical to the accuracy and execution of your proposed systems.
Let's begin by learning some of the strategy components
Components of the eSignal strategy system.
Fill Type Constants
- Strategy.CLOSE - uses the closing price as the fill price.
- Strategy.LIMIT - uses the StopOrLimit price as the fill price.
- Strategy.MARKET - uses the open price as the fill price.
- Strategy.STOP - uses the StopOrLimit price as the fill price.
Fill Bar Constants
- Strategy.THISBAR - use the OHLC values of the current bar to determine the fill price in conjunction with the Fill Type.
- Strategy.NEXTBAR - use the OHLC values of the next bar to determine the fill price in conjunction with the Fill Type.
LotSize Constants
- Strategy.DEFAULT - uses the Default LotSize as the number of shares.
- Strategy.ALL - typically used with Strategy.doSell or Strategy.doCover.
Specifies that all shares being held be sold/covered.
Strategy Functions
Executing Strategy Orders
- Strategy.doLong (Description, Fill Type, Fill Bar, LotSize, StopOrLimit)
Returns true if filled, false if not filled. - Strategy.doSell (Description, Fill Type, Fill Bar, LotSize, StopOrLimit)
Returns true if filled, false if not filled. - Strategy.doCover (Description, Fill Type, Fill Bar, LotSize, StopOrLimit)
Returns true if filled, false if not filled. - Strategy.doShort (Description, Fill Type, Fill Bar, LotSize, StopOrLimit)
Returns true if filled, false if not filled.
Returning Strategy Conditions
- Strategy.isInTrade ()
Returns true if currently in a trade (long or short). Otherwise false. - Strategy.isLong ()
Returns true if currently long. Otherwise false. - Strategy.isShort ()
Returns true if currently long. Otherwise, false.
Controlling Strategy Stops
- Strategy.setStop (dStop)
Set a stop order. This will close out the active position if the stop order
price is reached or exceeded. - Strategy.clearStop () Clear (remove) the current stop order.
Controlling Position Size/Management
- Strategy.getPositionSize () Returns the number of shares currently held. This number is negative if short, positive if long. E.g.: -100 is short 100 shares. 100 is long 100 shares.
- Strategy.getDefaultLotSize () Returns the default lot size as set by the user before starting the BackTest.
Developing Entry/Exit Strategies.
The first step in developing strategies is to learn to use the different types of strategy function calls. There are four primary types of strategy functions they are.
- Strategy.doLong - Enters a long (Buy) order
- Strategy.doSell - Exits a long (Buy) order
- Strategy.doShort - Enters a short (Sell) order
- Strategy.doCover - Exits a short (Sell) order
These four basic functions allow users to enter, exit and maintain stops and well explain the use of these functions in more detail later in the guide. Simply put, these are all you need to learn to begin developing your custom strategies.
A simple example would be the following:
if ( /* Buy Signal */ ){ Strategy.doLong(.); } if ( /* Sell Signal */ ){ Strategy.doShort(.); }
Of course, there is more to it than simply these two calls, but as an example, this illustrates how to enter orders using basic eSignal functions.
Exiting trades is as simple as adding the exit function calls and testing for a few conditions. The developers at eSignal have also provided additional functions for testing (or returning) strategy conditions. These additional functions include the ability to determine if the strategy system is currently in a trade (or not) and if we are long or short. This seems simple enough huh? The functions are listed below
- Strategy.isInTrade ()
Returns true if currently in a trade (long or short). Otherwise false. - Strategy.isLong ()
Returns true if currently long. Otherwise false. - Strategy.isShort ()
Returns true if currently long. Otherwise, false.
Combining these functions allows developers to develop complex trading strategies based on the current strategy condition.For example, lets start with the code above and modify it to exit a previous trade if we have an open position before entering our new trade. Developers must remember to exit open trades before entering reverse orders. This way, the eSignal strategy will not continue adding positions to other open positions.
The example is as follows
if ( /* Buy Signal */ ){ if ((Strategy.isInTrade() == true) && (Strategy.isShort() == true)) { Strategy.doCover(.); // Exits prior SHORT trade } Strategy.doLong(.); // Enters new LONG trade } if ( /* Sell Signal */ ){ if ((Strategy.isInTrade() == true) && (Strategy.isLong() == true)){ Strategy.doSell(.); // Exits prior LONG trade } Strategy.doShort(.); // Enters new SHORT trade }
In this example, we are first identifying a new trade trigger (buy or sell), then testing to see if we are currently in a previous trade or not (Strategy.isShort or Strategy.isLong) and acting on this information (by covering or selling the current open trade), then entering our new order.
Let's look at some real code. In this example, a variable nNewTrade is used to determine if a new trading signal has been issued. nNewTrade will be set to '1' if a trading signal is triggered or be left to '0' if not. Sample code is included for nNewTrade for readers to learn how to use this type of function.
The code below illustrates the use of variables and the Strategy features to enter, exit and reverse trading direction easily.
var nNewTrade; // New Trade Trigger 0 = OFF / 1 = ON //nsignal returns 1 for bullish, -1 for bearish var nsignal;//returns the direction of the trading signal // Identify new trade signals if ( /*Buy Condition*/ ){ nNewTrade = 1; // New Trade Trigger nsignal = +1; //Buy Signal } if ( /*Sell Condition*/ ){ nNewTrade = 1; // New Trade Trigger nsignal = -1; // Sell Signal } // Execute Trades ONLY if nNewTrade is triggered .... if (nNewTrade == 1){ //Execute New Trade // new or reversed trade position if (Strategy.isInTrade() == true){ if ((nsignal > 0) && (Strategy.isShort() == true)){ Strategy.doCover("Exit Short", Strategy.MARKET, Strategy.NEXTBAR); Strategy.doLong("Rev Long", Strategy.MARKET, Strategy.NEXTBAR); } if ((nsignal < 0) && (Strategy.isLong() == true)){ Strategy.doSell("Exit Long", Strategy.MARKET, Strategy.NEXTBAR); Strategy.doShort("Rev Short", Strategy.MARKET, Strategy.NEXTBAR); } } else{ // Not in Trade Now if ((nsignal > 0)){ Strategy.doLong("Go Long", Strategy.MARKET, Strategy.NEXTBAR); } if ((nsignal < 0)){ Strategy.doShort("Go Short", Strategy.MARKET, Strategy.NEXTBAR); } } // end if IN TRADE nNewTrade = 0; // Turn off NEW TRADE switch } // END EXECUTE NEW TRADE
Notice the use of the Strategy.MARKET and Strategy.NEXTBAR options in all of the trade execution functions. The Strategy.MARKET option indicates we expect to enter at the OPENing price. The Strategy.NEXTBAR statement tells the eSignal strategy to enter on the next bar.
This example code will only execute the strategy functions if the variable nNewTrade is switched on (set to the number 1). At the end of the conditional if statement, we need to switch the nNewTrade variable back to 0 (zero) and wait for another new trading signal to appear.
The only difference between the first group of conditions and the second is the if (Strategy.isInTrade() == true). The first condition tests if the Strategy is currently in a trade, then executed the Strategy functions to effectively reverse positions. The second condition occurs if the strategy is not in a trade and executes the functions to enter new positions.
Chapter Conclusion
The use of the Strategy.doLong, Strategy.doShort, Strategy.doSell and Strategy.doCover are the primary functions for executing trading orders for back-testing. There are variations of these functions (which will be discussed later) to handle different types of order executions, but these are the only four functions any developer needs to remember to begin developing complex strategies.
The inclusion of the Strategy Condition statements (Strategy.IsLong, Strategy.IsShort and Strategy.IsInTrade) are used to test and determine the current Strategy conditions. These conditions should be used to verify current strategy conditions before executing new strategy trading functions.
These are the basic building blocks of the eSignal Strategy (back-testing) functions. These seven (7) basic functions allow developers to build and test complex trading strategies. Developers can develop any number of entry systems using technical indicators, price patterns and other custom analysis techniques. eSignal provides developers with a wealth of built-in indicators and the ability to easily program your own.
Congratulations! You have reached the first half of your new trading system. We have gone through the entry signals and strategy functions. Next, well learn to use these same functions as exit signals, trailing stops and profit targets or exit signals.
Using Limit Orders And Adding Profit Targets
Adding profit targets is as simple as adding an additional clause before the entry code to test if the market is above or below our defined profit target. Developers would continue to use the Strategy.IsLong, Strategy.IsShort and Strategy.IsInTrade to identify is the strategy system is in a trade or not and would have to setup specific variables to handle the profit targets.
Beyond these additional variables, the code is relatively simple. The first thing we need to do is develop the variables to handle the profit target. Well continue to use nNewTrade and nsignal as in the previous example to identify new trading signals and trading direction.
Before we get into the code, we need to understand a little bit about how eSignal tests and runs through the dataset (the bars on the chart). eSignal EFS files run our main() function for every bar on the chart (from left to right). Thus, if your system generates a trade on a bar, we won't know the entry price of this trade until the next bar. To address this, we must build our EFS files to be able to handle this issue by using Global Variables.
Global variables are declared exactly the same as local variables. The difference is they are declared OUTSIDE the premain() and main() functions. These variables can be modified by your code and will pass to the next instance of the main() function. Thus, global variables should be used for system wide variables or for values that need to remain through multiple trading bars.
Another important fact is the code structure. Remember the EFS code is executed in order (top to bottom) for each bar. If our system executes an entry order, we will not be filled until the next bar. Thus, we need to set a trigger to record the entry price and then test for our profit target exit before we test for any new trade conditions. Using this structure, the code passes variables from one bar to another that triggers the execution of specific features (profit targets, stop trailing levels and others).
Lets review the following code step by step
* Note that the following example code is not a stand alone formula in itself. The underlying study used to determine the buy and sell conditions need to be incorporated into the code. To see an example of a completed back testing formula using this example code with a moving average study, please download the formula at the following link. Sample_ProfitTargets.efs
/*---------------------------------------------------------- Sample Profit Target Code
---------------------------------------------------------- Declare new global variables for the code. Notice we are Only adding two new variables to handle the profit targetFunction - nTradeEntryPrice and ProfitTarget1.NTradeEntryPrice = the expected price of our entry trade. ProfitTarget1 = the projected profit target level (points). ----------------------------------------------------------*/ var nNewTrade; // New Trade Trigger 0 = OFF / 1 = ON var nsignal; // returns the direction of the trading signal var nTradeEntryPrice; var ProfitTarget1 = 5.0; function preMain(){ setPriceStudy(true); setStudyTitle("Sample Profit Code"); setCursorLabelName("PT CODE"); } function main(){ /*---------------------------------------------------------------- // If new trade, get entry price - used for our profit target ---------------------------------------------------------- This portion of the code identifies if a new trade has been issued and records the entry price of our trade. If no new trade has been triggered (nNewTrade == 1), then this portion of the code is ignored. ----------------------------------------------------------*/ if (Strategy.isInTrade() == true && (nNewTrade == 1)){ // This sets the expected entry price of the current short trade nTradeEntryPrice = open(); // This switches off the nNewTrade variable nNewTrade = 0; // Turn off NEW TRADE switch } /*---------------------------------------------------------------- // Test for Profit Target Breach (ProfitTarget1) ---------------------------------------------------------- This portion of the code identifies if our profit target has been reached and exits our trades. ----------------------------------------------------------*/ if (Strategy.isInTrade() == true && (Strategy.isShort() == true)){ // Check if the profit target has been reached/breached if (low() <= (nTradeEntryPrice - ProfitTarget1)){ // Profit Target Breached, Execute Cover order. Strategy.doCover("Short PT1 Exit", Strategy.STOP, Strategy.THISBAR, Strategy.getDefaultLotSize(), (nTradeEntryPrice - ProfitTarget1)); } } if (Strategy.isInTrade() == true && (Strategy.isLong() == true)){ // Check if the profit target has been reached/breached if (high() >= (nTradeEntryPrice + ProfitTarget1)){ // Profit Target Breached, Execute Cover order. Strategy.doSell("Long PT1 Exit", Strategy.STOP, Strategy.THISBAR, Strategy.getDefaultLotSize(), (nTradeEntryPrice + ProfitTarget1)); } } /*----------------------------------------------------------------// Identify new trade signals ----------------------------------------------------------------- */ if (Strategy.isInTrade() == false){ if ( /*Buy Condition*/ ){ nNewTrade = 1; // New Trade Trigger nsignal = 1; // Buy Signal - Trade Type } if ( /*Sell Condition*/ ){ nNewTrade = 1; // New Trade Trigger nsignal = -1; // Sell Signal - Trade Type } } /*---------------------------------------------------------------- // Execute Trades ONLY if nNewTrade is triggered .... ----------------------------------------------------------------- */ if (nNewTrade == 1) { //Execute New Trade // new or reversed trade position if (Strategy.isInTrade() == true){ if ((nsignal > 0) && (Strategy.isShort() == true)){ Strategy.doCover("Exit Short", Strategy.MARKET, Strategy.NEXTBAR); Strategy.doLong("Rev Long", Strategy.MARKET, Strategy.NEXTBAR); } if ((nsignal < 0) && (Strategy.isLong() == true)){ Strategy.doSell("Exit Long", Strategy.MARKET, Strategy.NEXTBAR); Strategy.doShort("Rev Short", Strategy.MARKET, Strategy.NEXTBAR); } } else{ if ((nsignal > 0)){ Strategy.doLong("Go Long", Strategy.MARKET, Strategy.NEXTBAR); } if ((nsignal < 0)){ Strategy.doShort("Go Short", Strategy.MARKET, Strategy.NEXTBAR); } } // end if IN TRADE } // END EXECUTE NEW TRADE }
After reading through the code above, one might ask, Why is the PROFIT TESTING code above the ENTRY code? The answer is based on CODE ORDER. Developing any type of strategy in eSignal requires strict understanding of the order of processing the code. All EFS files are processed from left to right on the chart (oldest bar to newest bar) and the code is processed from top to bottom. When we develop the trading system logic, we must develop the code to process in a specific order (using local and global variables). What is the difference between local variables and global variables?
The Global Variables (defined at the top of this code) continue their value throughout the execution of this EFS file. If the global variable is set to 3, it will continue to be 3 through the continued execution of this script. Global variables are created in the first instance of this script (the first time it is run) and are available throughout the execution of this script.
Local variables are declared within a function (like main() or someotherfunction()), and are created for each instance of the function. Local variables are also destroyed after the function completes its task. Once the local variable is destroyed (after the function is completed), it does not retain its value as the EFS file continue to process on each bar.
So, as developers, we want to use global variables to control many of the EFS files main variables (like trading system logic, profit target levels, stop price levels and other things). We would also want to use local variables for localized functions or values that do not need to carry-forward across the life of our code.
The reason we place the profit target (and in the future stop price detection) before the entry trade logic is because the EFS file will calculate top to bottom for each bar. Developing in this manner will allow the EFS trading system to logically step through our logic (identify trading signals, enter our trade, test for profits/stops). The use of if (Strategy.isInTrade() == true) allows us to make the logic for the profit target conditional on the fact that we have entered a trade. Doing this makes it easier for developers to create conditions on segments of their code.
One other important fact to mention is the inclusion of (Strategy.isInTrade() == false) before our entry conditions. The reason we added this additional condition to our entry code is because we dont want our entry triggers to go off while we are in a trade. This condition sets our entry triggers to only fire when we are not in a trade.
Adding Trailing Stop Levels to our Code.
To add trailing stop levels to our code, it is as simple as adding our profit targets. We simply add the necessary code just below our profit target code and add any necessary variables to our global variable declarations. We need to use the same type of limit orders as used for our profit targets, so the code for trailing stops will look very similar to the profit target code.
The only additional code we need to add is to calculate and set the trailing stop levels. This code should be added below the test for a stop breach as we want to test the current stop levels for a price breach, then set new trailing stop levels.
Lets look at some more code
Our example code will place a trailing stop point below the previous bars low (for long trades) and point above the previous bars high (for short trades).
* Note that the following example code is not a stand alone formula in itself. The underlying study used to determine the buy and sell conditions need to be incorporated into the code. To see an example of a completed back testing formula using this example code with a moving average study, please download the formula at the following link. Sample_TargetStop.efs
/*---------------------------------------------------------- Sample Profit Target/Trailing Stop Code ---------------------------------------------------------- Declare new global variables for the code. Notice we are Only adding one new variables to handle the stop loss Function - nStopLevel.nStopLevel = our stop/trailing stop level. ----------------------------------------------------------*/ var nNewTrade; // New Trade Trigger 0 = OFF / 1 = ON var nsignal; // returns the direction of the trading signal var nTradeEntryPrice; var ProfitTarget1 = 5.0; var nStopLevel; function preMain(){ setPriceStudy(true); setStudyTitle("Sample Profit Code"); setCursorLabelName("PT CODE"); } function main(){ /*---------------------------------------------------------------- // If new trade, get entry price - used for our profit target ---------------------------------------------------------- This portion of the code identifies if a new trade has been issued and records the entry price of our trade. If no new trade has been triggered (nNewTrade == 1), then this portion of the code is ignored. ----------------------------------------------------------*/ if (Strategy.isInTrade() == true && (nNewTrade == 1)){ // This sets the expected entry price of the current short trade nTradeEntryPrice = open(); // This switches off the nNewTrade variable nNewTrade = 0; // Turn off NEW TRADE switch } /*---------------------------------------------------------------- // Test for Profit Target Breach (ProfitTarget1) ---------------------------------------------------------- This portion of the code identifies if our profit target has been reached and exits our trades. ----------------------------------------------------------*/ if (Strategy.isInTrade() == true && (Strategy.isShort() == true)){ // Check if the profit target has been reached/breached if (low() <= (nTradeEntryPrice - ProfitTarget1)){ // Profit Target Breached, Execute Cover order. Strategy.doCover("Short PT1 Exit", Strategy.STOP, Strategy.THISBAR, Strategy.getDefaultLotSize(), (nTradeEntryPrice - ProfitTarget1)); } } if (Strategy.isInTrade() == true && (Strategy.isLong() == true)){ // Check if the profit target has been reached/breached if (high() >= (nTradeEntryPrice + ProfitTarget1)){ // Profit Target Breached, Execute Sell order. Strategy.doSell("Long PT1 Exit", Strategy.STOP, Strategy.THISBAR, Strategy.getDefaultLotSize(), (nTradeEntryPrice + ProfitTarget1)); } } /*---------------------------------------------------------- This portion of the code tests for a stop level breach and executes trades accordingly. ----------------------------------------------------------*/ if (Strategy.isInTrade() == true && (Strategy.isShort() == true)){ // Check if the profit target has been reached/breached if (high() >= nStopLevel){ // Stop Breached, Execute Cover order. Strategy.doCover("Short Stop Exit", Strategy.STOP, Strategy.THISBAR, Strategy.getDefaultLotSize(), nStopLevel); } } if (Strategy.isInTrade() == true && (Strategy.isLong() == true)){ // Check if the profit target has been reached/breached if (low() <= nStopLevel){ // Stop Breached, Execute Sell order. Strategy.doSell("Long Stop Exit", Strategy.STOP, Strategy.THISBAR, Strategy.getDefaultLotSize(), nStopLevel); } } /*---------------------------------------------------------- This portion of the code calculates and sets the new stop levels. ----------------------------------------------------------*/ if (Strategy.isInTrade() == true && (Strategy.isShort() == true)){ nStopLevel = high(-1) + 0.25; } if (Strategy.isInTrade() == true && (Strategy.isLong() == true)){ nStopLevel = low(-1) - 0.25; } /*---------------------------------------------------------------- // Identify new trade signals ----------------------------------------------------------------- */ if (Strategy.isInTrade() == false){ if ( /*Buy Condition*/ ){ nNewTrade = 1; // New Trade Trigger nsignal = 1; // Buy Signal - Trade Type } if ( /*Sell Condition*/ ){ nNewTrade = 1; // New Trade Trigger nsignal = -1; // Sell Signal - Trade Type } } /*---------------------------------------------------------------- // Execute Trades ONLY if nNewTrade is triggered .... ----------------------------------------------------------------- */ if (nNewTrade == 1){ //Execute New Trade // new or reversed trade position if (Strategy.isInTrade() == true){ if ((nsignal > 0) && (Strategy.isShort() == true)){ Strategy.doCover("Exit Short", Strategy.MARKET, Strategy.NEXTBAR); Strategy.doLong("Rev Long", Strategy.MARKET, Strategy.NEXTBAR); nStopLevel = low(-1) - 0.25; } if ((nsignal < 0) && (Strategy.isLong() == true)){ Strategy.doSell("Exit Long", Strategy.MARKET, Strategy.NEXTBAR); Strategy.doShort("Rev Short", Strategy.MARKET, Strategy.NEXTBAR); nStopLevel = high(-1) + 0.25; } } else{ if ((nsignal > 0)){ Strategy.doLong("Go Long", Strategy.MARKET, Strategy.NEXTBAR); nStopLevel = low(-1) - 0.25; } if ((nsignal < 0)){ Strategy.doShort("Go Short", Strategy.MARKET, Strategy.NEXTBAR); nStopLevel = high(-1) + 0.25; } } // end if IN TRADE } // END EXECUTE NEW TRADE }
We've just created our first strategy code (complete with entry triggers, exit triggers, profit targets and a trailing stop system). When we approach the development process in stages, it becomes a very simple process. Below, is development process for future strategy builders:
- Identify entry triggers that appear to work for your system. Develop simply the entry triggers and test them.
- Develop the entry logic code and supporting net-reverse code for your system. Make sure everything is working perfectly before attempting to develop further.
- Identify a stop-loss system you wish to deploy into your code. Create the necessary variable to handle your stop-loss system and add the stop-loss segment to your code. Test the system to make sure the stop-loss system is working perfectly.
- Identify a profit target system you wish to deploy into your code. Create the necessary variables to handle your profit target system and add the new code segment to handle the profit targets.
- Test, test, test.
Once you have a system that is working the way you expect it to, then you can begin to run some detailed back-tests on different markets. You may also find that your stop-loss and profit target systems need to adapt to market volatility. The examples are fixed meaning they do not adapt to market volatility. Developing adaptive profit target and stop-loss levels simply requires additional coding and more complex mathematics.
The next guide, The Guide to Developing Graphics in eSignal will instruct you on how to add graphical instruments to your strategy (trading system) so you can see what the system is doing on the chart in the form of graphical icons. Adding graphics to your strategy will provide you with the following benefits:
- The ability to watch your system trade on a chart.
- The ability to see where your current stop levels are.
- The ability to see where your profit targets are reached.
- The ability to modify your trading system based on your observances of how it reacts to market price action.
The sample code throughout this guide can be used as a template for your own custom strategy. The only thing you need to do is modify the variable setting and create your own trade entry triggers. A suggestion would be to create a simple entry trigger using a moving average or price pattern.
Strategy Building Tips.
- Use Strategy.NEXTBAR for all ENTRY and EXIT orders that are not LIMIT or STOP orders. This assures your system is entering at an actual price and is not looking forward.
- Use Strategy.THISBAR for all STOP ORDERS and LIMIT ORDERS after testing for the correct conditions.
- Stop Trailing and Stop Breach systems must test for HIGH/LOW price breaches of the stop price and OPEN price breaches. This will provide an accurate representation of a stop/limit exit strategy.
Written by Brad L. Matheny for eSignal, A division of Interactive Data Corporation, November 2002.