The Basics of Writing Experts in NTL+
This article is meant for programmers who are planning to write their first expert in NTL+. It will cover a number of features of experts creation, the knowledge of which will help avoid lots of mistakes and save the time spent on writing and debugging the script (the term 'script' in NTL+ is common to three types of programs: experts, indicators and utilities).
Creating an expert
In order to create a new expert, go to the Navigator window, right-click on the Advisors folder and choose the Create function from the context menu. You will be offered to enter a name for the new expert. Please note that the name of the expert complies with the standard rules for OS Windows filenames: a name cannot contain the following characters: *|\:"<>?/ .
After a new expert file has been created, you will see the initial template with 3 functions: Initialize(), Run(), DeInitialize(). Each of these functions is run at a certain moment of expert's life. Thus, the programmer has no need to call them, as the process is automatic.
Immediately after starting an expert, the Initialize() function is run right after the start of an expert and is intended for initializing parameters. It is worth pointing out that trading operations should not be performed in the Initialize() function, as the values of trading parameters (for example, Bid and Ask prices, account data, information on positions and orders) may not be determined at the moment Initialize() is being launched.
Every time a tick comes, the Run() function is called; it is this function that is supposed to contain the main script code ,. In this code, it is possible to analyze the current market situation, make decisions on the necessity of performing trading operations, either do mathematical calculations. The data processing for every tick should not exceed 10 seconds, otherwise the program will automatically stop the user expert.
When an expert is stopped, the DeInitialize() function is called, which takes some final steps before the script stops. Many scripts do not require any steps under initializing and deinitializing, in which case the Initialize() and DeInitialize() functions can be left blank or completely removed from a script (the terminal will not call them).
Drawing up an algorithm and writing an expert code
The first stage is the development of an algorithm that will be run by your trading expert. This algorithm should have a clear structure and detailed comments, which will ease further debugging, modification and optimization.
It is necessary to define the trading criteria for opening long and short positions, the conditions under which the positions will be closed, and how the number of open positions will be controlled and limited. If your expert is to set pending orders (including OCO and activation ones), you should first consider the possibility of removing or modifying the already placed orders.
The success of an expert as a whole depends on the right choice the trading criteria for opening/closing positions and setting orders. This way, strategy development is essential for a good expert. Trading criteria can be created on the basis of technical indicators, price levels analysis and other calculations based on your understanding of effective trading.
Mind your manual intervention with the operation of an expert; what if the expert opens a position, and you close it in the terminal? Consider, whether the expert can switch to any unplanned condition, which will lead to malfunction.
For proper functioning, you will need to analyze the status of open positions and placed orders at every tick. Of use will be the properties Deals.Total and Orders.Total that properties the number of open positions and placed orders, and the Deals.Select() and Orders.Select() methods that are used for choosing a position or an order.
It is also worth paying attention to the recovery of the expert's work after the restart of the terminal: whether it switches to the same condition it had before the restart. Take into account the situations when some positions between restarts can be closed by the stop-loss or take-profit, and the orders can be activated, which will entail the opening of new positions.
You should foresee actions on the occurrence of standard errors and situations, for example, lack of funds for opening positions or an attempt to set orders that are too close to the current price.
Access to the account status data can be granted via the Account object and its properties. For instance, the Account.Balance property is used to obtain the current account balance, and the Account.MarginFree property – to obtain the free margin amount.
Note the line return -1, execution of this operator stops the expert forcibly.
Working with the quotes history
The use of timeseries allows you to get the quotes history of the current symbol in order to analyse a certain past market situation. Timeseries represent a group of arrays: Open – opening bar prices, Close – closing bar prices, High – highest bar prices, Low – lowest bar prices, Time – opening bar time and Volume – tick volume of the bar (the number of ticks per bar). All timeseries are relevant to the symbol’s chart, on which the expert is running, and the current timeframe. Timeseries have reverse indexing: the latter element (latter in the terms of time) has a 0 index, and the first element – an index equal to Bars.Total(Chart.Symbol,Chart.Interval)-1.
If it is necessary to obtain data for any other symbol, you will need to use the Bars object. Its methods Open(), High(), Low(), Close(), Volume(), Time() are similar to the corresponding timeseries methods, the only difference being that you should specify the symbol, the bar size (interval, timeframe) and the number of the bar that you want to obtain values for. Before running a script, you need to subscribe to all the symbols the script works with. In order to do this, open the MarketWatch window, right-click on an empty field, select Subscribe from the context menu, click on the desired symbols and press "OK".
In experts, you may need to determine when a new bar opens or when the formation of a previous bar is complete. For this purpose, you can use the Volume global array. The value of the Volume is increased by one at each tick and resets to 1 after the first tick of a new bar. Thus, to determine the opening moment of a new bar, the following construction may be used:
The work of Run() can easily be terminated using the return operator for all values for the Volume except for 1:
Another alternative method is analyzing the bar opening time, if the time for the last two ticks matches, you can exit from the function by using the return(0) operator. At the formation of a new bar, we get different values for Time on the current and previous ticks – it is this event that means a new bar opening.
Testing an expert
The Advisor Tester is intended for evaluating the expert effectiveness and checking its functioning. To start the tester, select View -> Advisor Tester in the main menu. There are tabs for switching between the sections of the tester at the top of its window:
- Parameters – tester parameters
- Results – tabular presentation of the information on positions
- Equity Graph – change of balance and equity
- Journal – journal file
On the Parameters tab, you can specify the following parameters that have impact on the testing process:
- The drop-down Advisor list – choosing an advisor for testing.
- Properties button – calling the expert parameters for editing. If the expert does not have parameters, specified with the extern keyword, then the button will be disabled.
- Symbol – symbol for testing*.
- Interval – interval(timeframe) for testing.
- Spread – choice of a spread value. Current spread option corresponds to the current value of the spread. You can also enter any non-negative value in the Spread field.
- Method – testing method. It can be one of the following options Control Points or Open Price Only. When selecting Control points 4 ticks are automatically generated for each bar (the sequence is given further). The ticks correspond to the Open, Low, High, Close prices for bars with the opening price lower than the closing price; for bars with the opening price higher than the closing price – Open, High, Low, Close.
For the Open Prices Only method, all trades are only made at the opening prices. The following elements of timeseries at zero index will be equal to each other Open = High = Low = Close, the values for non-zero indexes of the timeseries coincide with the corresponding values of timeseries for the Control Points method.
- The Limit Dates check box is used to turn on a limitation for the tested range of dates
- Input dates fields From and To are used to specify the date range for testing. Default values correspond to the loaded history range for the specified symbol and interval.
- Initial Deposit – initial deposit at the beginning of testing.
- The Enable visual mode check box is used to display a chart with marked opening and closing positions, as well as moments of orders' setting for the last tested position while the testing is in progress.
- The Enable expert logs check box is used to print output from the System.Print() function to the Journal tab.
- The Start/Stop button starts and stops the testing.
Checking the Enable visual mode and Enable expert logs checkboxes affect the testing speed, so it is recommended to mark them only when it is necessary.
* Testing of multicurrency experts is not fully supported at the moment. If you test the advisor that works with deals on several currencies and the loaded history range for quotations of these symbols does not match, the bid and the ask prices, timeseries and the values returned by the methods of the Barsobject can be zero.
The Results tab displays information on all opened and closed positions during the testing process. The table with closed positions is located at the top. It displays the following information:
- Number # – an ordinal number of a closed position. #1 is assigned to the first closed deal, #2 - to the second, etc.
- Deal ID – closed position identifier
- Symbol – symbol on which the position was opened
- Volume1 – position volume in the base currency units
- Volume2 – position volume in the quoted currency units
- Open rate – open price
- Open time – open time
- Stop loss – stop loss level (shown only when specified)
- Take profit – take profit (shown only when specified)
- Last swap – current position swap
- Rate close – close price
- Time close – close time
- Profit – profit of the position
- Balance – balance after the position is closed*
- Equity – equity after the position is closed *
* These columns are displayed in the Show extended columns mode. This mode can be enabled via the context menu of the table.
The bottom table displays positions that were not closed as at the end of testing. The headings of the columns coincide with the headings of the Closed Positions table columns, except for the missing columns linked with closed positions.
The bottom status line displays information on the balance, equity, margin requirements and free margin as at the moment when the testing is complete.
The graph displays the change of the balance and equity, depending on the number of a closed position. Double-click on the chart shows the relevant deal in the table of Closed positions.
The Journal tab displays text output of the System.Print() function used in an expert. If the Enable expert logs check box on the Parameters tab is off, the journal will only have the start and stop expert commands.
Debugging the script
Debugging the script is rarely done without using the System.Print() function, which displays information on the Journal tab of the Toolbox window (or on the Journal tab of the tester window). However, it is helpful to know that when you run an expert, the displayed information is also duplicated in the log file in the username\Documents\NeTTradeX Advisors\logs folder. Log files can be opened with any text editor that supports the txt format. The log file contains records with the time of an event, return code and text message.
Adding a value processing for System.LastError property enables you to analyze the situation when your expert malfunctions. This property stores the code of the last error associated with trading operations. This property has one important feature: each subsequent trading operation changes the property in accordance with the result of its work. Thus, if you have a script that makes a request for a deal with a wrong parameter and then places an order with correct parameters, the zero value (no error) will be recorded in the System.LastError. Therefore the System.LastError property value should be received BEFORE the next trading operation (in our case, placing an order), so it will store the code of the occurred error during the deal making process. The System.ErrorDescription() method is used to receive a text description of an error, it brings back the line with a text description of the problem; the only input value represents the numeric error code received via System.LastError.
When debugging is under way, the IsStopped property of the System object can also be useful. It has the true value, if the execution of the program on the last coming tick took longer than 7 seconds. With the help of this property, the programmer is informed that the script will soon be forcibly stopped (after 3 more seconds), thereby some time is given so that the program can perform certain operations related to the correct completion of the script.
In conclusion, it should be pointed out that the process of expert creation can be divided into two essential stages: developing an algorithm and its coding. The more careful the first stage is worked out, the easier and the faster the implementation of the second is. The present article has covered certain issues concerting algorithm creation and practical solutions making it possible to simplify your first acquaintance with NTL+ and its capabilities for writing trade experts.