Copyright © Henwood Software Corp.
Last Update 9-2-08
tp.timeToQuit() returns true)
| start | the number of days ago the backtest will start |
| end | the number of days ago the backtest will end |
| frequency | frequency of data that will be used for backtest. frequency codes |
| systems | Contains the systems (Python scripts) that will be run in this backtest. You can type the systems and paths, but it is much easier to just use the Browse button and select from the systems directory which systems you would like to run. |
| symbols | Each symbol in this box will be tested against each system in the systems field. |
| specify symbols | Check this box if you would like to type each symbol in the symbols field. |
| use all symbols in database | Select this option if you would like to test every symbol, of the specified frequency, in the data directory against the given systems. |
| range | Select this option if you would like to type only 2 symbols in the symbols field, and have the application test those two symbol and all those symbols in the data directory that fall between them, in alphabetical order. |
| date | date and time that backtest ended |
| system | name of system tested |
| symbol | symbol returned by tp.getSym() during backtest |
| profit/loss | total profit/loss |
| optimization | optimization value for test, see Optimization |
| start | periods ago test started |
| end | periods ago test ended |
| frequency | data frequency returned by tp.getFreq() during test |
| order count | total number of orders placed during test |
| trade win % | percentage of trades that were profitable during test |
You can sort any of the above fields by clicking the column label.
Right clicking anywhere on the chart produces a pop-up menu:
| add item | Opens the Add Item Dialog for adding a new item to the chart. |
| add graph | Creates a new graph at the bottom of the chart. |
| options | Open options dialog for this chart. |
| clear | Delete all items from chart. |
| edit indicator | brings up sub-menu of all items on chart. Select one to edit it. |
| delete indicator | brings up sub-menu of all items on chart. Select one to delete it. |
| Close and Save Button | ***REMOVED*** |
| Quick Symbol Entry | The 2 text boxes located at the top left of the window are used for quickly changing the primary symbol of the chart. The primary symbol is simply the first item that was created on the chart. Enter the symbol in the first box and the exchange, if required, in the second. Generally you do not need to enter and exchange for stocks, that will be determined automatically, but it is required for futures. |
| left mouse click over item or graph | Selects item or graph. Selected item or graph turns bright green. |
| escape key | Deselect selected item or graph, if any. |
| del key | Deletes selected item or graph, if any. If a graph is deleted all items it contains will also be deleted. |
| middle mouse button over item | Brings up edit box for clicked item. |
| page up | increases size of selected graph by 10% |
| left arrow key | change primary symbol to proceeding symbol in data directory |
| right arrow key | change primary symbol to next symbol in data directory |
| up arrow key | save current chart and load chart proceeding chart in chart directory |
| down arrow key | save current chart and load next chart in chart directory |
| Brokerage | Select desired trade service provider, and enter username as password. | |
| Data | Select desired data service provider, and enter username as password. | |
| TradeProgrammer | Enter the email address and password for your TradeProgrammer account. | |
| connect on startup | If checked the application will automatically connect to selected trade and/or data provider when started. | |
| save passwords | All passwords will be saved, and reloaded the next time you open the Connection Settings Dialog. |
Field descriptions.
| daily | the starting date for daily data stored int database |
| intraday | the starting date for intraday data stored in database |
| update bar data | download all missing historical and intraday data in database |
| tick | the starting date for tick data stored in database |
| update tick data | download all missing tick data in database |
| exchange | Use this to add/remove all symbols from a major US exchange: NYSE, AMEX, or NASDAQ. First select an exchange, then click "add all" or "remove all." |
| update symbol files | Update the list of symbols on NYSE, AMEX, and NASDAQ exchanges. This requires downloading several megabytes of information from nasdaq.com, and therefore should not be done often. Once every few months acceptable. |
| open data folder | Opens data directory, allowing you to view all symbol files stored there. |
Message Window 1 displays various messages sent by the TradeProgrammer application and trade or data service providers. Messages Window 2 displays messages sent by errors in your systems, or by calls to tp.text() from within your system.
Right clicking on a message window, then selecting "clear all" will delete all messages in the window.
| offline mode | If checked, authentication will be skipped at startup, allowing slightly faster startup. Trading and data features are disabled in offline mode. |
| automatic data remove time | ***DISABLED*** The number of seconds that will pass before unused data is removed from memory. If 0, unused data is never removed from memory. Adjust this to fit your charting and system behavior. |
| unattended mode | Set this option if you want hands free trading across multiple days. If set to true, application will automatically start your systems at the market open and stop them at the market close. Application will also take other necessary action to ensure trading is performed, just as starting trading/data software. You can specify the market open and close within this same options dialog. Note that while this feature is available it has not been tested sufficiently and is not recommended for live trading. |
| distinguish symbols by script | If checked, two scripts can use the same symbol without conflict. * |
| use test orders | When checked, all orders will not be sent to the API but will be stored in application. |
| market open hour/minute | The time when your trading begins. ** |
| market close hour/minute | The time when your trading ends. ** |
| data directory | the directory where your stock data is to be stored |
| system directory | All systems in this directory are run when choosing Start Systems from the main menu |
| max messages | the maximum number of messages you would like stored in Message Window 1 or 2 |
| trim data | removes data before and after market open and close you specify, must restart app after checking to take effect |
If you do however require this feature there is currently one limitation. If you close the TradeProgrammer application it will lose track of which script each open order and position belongs to. This will be fixed soon.
** This depends on when your trading day begins. Some may only trade between 9:30 am to 4 pm, the standard hours. Others might want to include the full possible extended period, 7 am to 6 pm or more. You should set the open time to a few minutes before your actual trading day open when using unattended mode to give your trading and data software time to initialize.
Displays information on all place orders.
| symbol | order symbol |
| size | order size, positive for buy order, negative for sell orders |
| price | order price |
| state | Order State. Possible values: open, filled. |
| destination | order ECN or exchange destination |
| id | order id |
| system | the system that sent this order |
| time | time order was sent |
| opt | optimization value for this order. See Optimization. |
You can sort any of the columns by clicking the column label.
| symbol | order symbol |
| size | Order size. Use positive value for buy order, negative value for sell order. |
| price | Order limit price. Set to 0 for market order. |
| IsTest | Check this box if you want the order to be a test order. |
Shows open position information.
| system | System which generated the position. |
| symbol | position symbol |
| size | Position size. Positive if long position, negative if short position. |
| test | 1 if order is a test order, 0 if a real order. Test orders are those generated by backtests, or when "User Test Orders" is checked in the options page. |
| opt | Optimization value which this position applies to. See Optimization. |
Enter a symbol, then enter number in only the fields you would like to change. Fields left blank are ignored. Then press ok to apply changes. After pressing ok the dialog remains open, allowing you to make more changes. When done, press cancel to close the dialog.
| script | the script used for the scan |
| symbol | the symbol used for the scan |
| result | the result produced by the scan by calling tp.scanResult(str) |
You can sort any of the columns by clicking the column label.
| symbols | the symbols that the given scripts will be scanned against |
| use all symbols in database | Check this box to scan again all symbols on disk. |
| scripts | The scan scripts. Each will be run against every symbol in symbols. Click browse to open scans directory and select a scan. |
| frequency | Frequency of data to be used in the scan. |
This window displays all systems currently running, along with status. Possible states are:
| ok | The system is running fine. |
| not responding | TradeProgrammer application has not heard from your system in a while. It could be temporarily waiting for data. If the status remains "not responding," the system has probably frozen due to a flaw in its coding. The safest way to close it in this situation is to close the application. |
| exited | The script has stopped executing. This can happen if you script exits intentionally, by calling exit, or breaking from the main loop after checking tp.timeToQuit(). It's also possible that due to a coding flaw it has exited unintentionally. |
| errors | the script has exited due to an error |
| stop | Flags the script for exit. The next call within the script to tp.timeToQuit() will return True. Note that after a script has been flagged for exit, any orders placed will be ignored. This prevents an order from being submitted after you want the script to stop, but before it has actually called tp.timeToQuit() and exited. |
Displays real-time info on placed trades. A row of information is displayed for each unique system/symbol combination. This means that if you have 2 different systems, each trading the same symbol, there will be two rows of information.
Column descriptions:
| system | system name |
| symbol | symbol |
| total pl | total realized and unrealized profit/loss* |
| realized pl | realized profit/loss |
| opt | Optimization value for this position |
Activation
The first time you use TradeProgrammer on a computer you will need to activate it. Open the Messages window if not already opened. You should see a message - "Not activated..." To activate, first enter your TradeProgrammer account login information in the Logins Settings Dialog by clicking Options->Login Settings from main menu. Then click from main menu Account->Activate. In you message window you should see "Activated." Now restart TradeProgrammer and it will be ready to use.
If the product was not activated you might receive one of these responses:
| Product previously activated and ready to use. |
| Activation failed. Email not found or password incorrect. Check and try again. |
| No more computers allowed for your account. Try clearing oldest activation or purchase an additional subscription. |
Authentication
Authentication is a process that occurs automatically every time TradeProgrammer is started. Upon success you will receive a "Authentication passed" message and "Authentication failed" upon failure.
Authentication works by collecting hardware information from your computer - the motherboard serial number, hard drive volume number (which changes when you reformat your hard drive), and network card MAC address. This information is sent to TradeProgrammer servers and checked against the information collected when the application was activated.
Switching Computers
If you decide you would like to use TradeProgrammer on a different computer than which it is currently activated for click from main menu Account->Clear Oldest Activation. Then install the TradeProgrammer software to the new computer and follow the first-time activation process.
You can backtest against one or more specified symbols or your entire base. You should close the Order, Position, and Trade Statistics Window before backtesting with a large database. The sheer number of orders generated (possibly thousands) will slow performance when these window are open.
At this time, using multiple symbols within the same system is not supported for backtesting, although it will work in live trading.
To start a backtest, click Tools->Backtest from the main menu. See the Backtest Dialog reference for information on setting the backtest parameters. When you are finished setting the parameters, click Ok and the backtest will begin. To view the backtest results, open the Backtest Results Window, from the main menu by clicking Window->Backtest Results.
You should read section Creating Systems to learn about the changes you must make to a system to make it ready for backtesting. Here is a summary:
if(!tp.testStep()) break
If you want to see where the orders for a backtest show up first run the back test then open a chart, add a price indicator such as candlestick, then add the "orders" indicator. Only the orders for the last backtest symbol can be viewed like this. If you already have such a chart open, you will need to refresh it, by resizing it, or closing and reopening it, to see the orders.
Batch Backtesting
If you want to peform multiple backtests and don't have time to start each test you can use Batch Backtesting feature . First make a script in the /tradeprogrammer/scripts directory. Use the tp.backtest() function for each test to be run. For example the script below will perform 5 tests total, one for each of the past 5 days, using tick data (frequency -4), symbol XYZ, and then print a message when done. How is this different from simply using a start and end of 0 and 5 using the backtest dialog? The below example will generate 5 sets of backtest results, one for each day, and the backtest dialog would create only one set. Creating a different set for each day allows you to compare the individual days to eachother.
Testing individual days is not the only use of batch builds. You can run any combination and number of tests you need.
for i in range(0, 5):
tp.backtest("c:/tradeprogrammer/system/mySystem.py", 'XYZ', -4, i, i)
print 'finished all testing'
Unlike backtests, which display scan results in terms of figures, such as profit/loss, the scan results are whatever you like. For example, if you are scanning for stocks who's volume greater than 1 million and less than 2 million, and you then want the volume displayed as the result, it would look as follows:
volume = tp.volume(tp.getSym(), tp.getFreq(), 1) if(volume > 1000000 and volume < 2000000): tp.scanResult(str(volume))
Scans use all the same functions a systems but they are much simpler and have no need for a loop. Two scans are never run at the same time in sperate threads and you cannot start a scan when systems are running. This eliminates the problem of variable name conflicts so unlike in systems you can use same-name variables in many different scan files without any special steps.
Optimization of trading systems is the process of making small adjustments to the variables of that system with the goal of maximizing the system's profits. Optimization is not automatic with TradeProgrammer. You must manually program which variables will be adjusted and what the adjusted values will be. Normally you will adjust just one or a few variables. For example if you are optimizing a SMA system you might try SMA 9, 10, 11 and 12 to see which produces the best results.
Optimization is achieved through the setOpt() function. This function stores the specified value for the script from which it was called. That value is then automatically passed along with all calls to order(), isPos(), and setPos(). Two calls to any of these functions with the same symbol, but with a different stored optimization value can be considered a call with two completely different symbols. There will be a different set of orders, positions, and profit loss for each symbol/optimization combination.
You can use optimization when backtesting or testing in real-time mode. The result will be displayed in the Backtest Results Window when backtesting or in the Trade Statistics Window when testing in real time. If you want to eventually use the same system your are testing for live trading you will need to write some conditional code in the system, using the tp.isTesting() function, to bypass the optimization code.
Below is a copy of the system presented in System Walkthrough only now it will trade sma 10, 11, and 12 in addition to sma 9.
1: import ind
2: import tp
3:
4: def sma9sys():
5: sym = ‘XYZ’
6: periods = 9
7: size = 100
8: freq=0
9:
10: if(tp.testing())
11: opts = [9, 10, 11, 12]
12: else
13: opts = [9]
14:
15: while not tp.timeToQuit():
16: for opt in opts:
17: tp.setOpt(str(opt)):
18: periods = opt
19:
20: if not tp.isOrder(sym, 0):
21: last = tp.last()
22: sma = ind.sma(sym, freq, periods, 0)
23: if last > sma and not tp.isPos(sym, 1):
24: tp.order(sym, size, 0)
25: if last < sma and not tp.isPos(sym, -1):
26: tp.order(sym, size*-1, 0)
27: if(!tp.testStep())
28: break
29: tp.sleep(1000)
This system has 8 additional lines of code:
| 10-13 | These 4 lines enable optimization when testing, and basically turn off optimization by setting only one opt value, 9, for live trading. |
| 9 | Fills an array with all the optimization values that will be used. |
| 11 | A for loop which will cycle through all optimization values. Basically, the system is run 4 times per cycle of the main loop, once for each optimization value. It is like running 4 systems in one. |
| 12 | Sets the optimization value for script, which is stored and effects later calls to order(), isOrder(), and isPos(). Notice that the value passed to setOpt() must be a string. |
| 13 | Adjust the periods of the sma used. Affects call to ind.sma() on line 17. |
import tp while not tp.timeToQuit(): #do system logicIf you plan on backtesting your system you should also make use of the tp.testStep() function. This function is used during backtests and has no effect when running your system live, allowing you to switch between backtesting and live trading without changing code. The testStep() function advances the data simulation by one interval and the break statement caused the main loop to stop when tp.testStep() returns 0, signalling that the backtest is over. The code now looks like this:
while not tp.timeToQuit(): #do system logic if(!tp.testStep()) breakAn important concept when trading systems is that you must property wait for open orders to close before proceeding with system logic. This is done using the tp.isOrder(symbol) function, which returns True if there is one or more unfilled orders for symbol, and False if not. For a detailed explanation of why this is important, see isOrder().
while not tp.timeToQuit(): if not tp.isOrder(symbol, 0): #do system logic if(!tp.testStep()) break
If you run many systems at the same time you run the risk of conflicts between same-name variables in different scripts. Each script is run in a different thread but threads share global memory. In python all variables which are not declared in functions or in another module are global. Unlike other languages, such as C, declaring a variable within a block creates a global variable rather than a variable who's scope is limited to that block.
Here's how it works in C/C++, which might be what you are used to:
thread 1int i = 1; cout << i;thread2
if(condition=true)
{
int i=4;
cout << i;
}
There is no conflict with the above code, thread 1's output is always 1, and thread 2's output is always 4. However in python it is different:
thread 1i = 1 print ithread 2
if(contrition=True) i = 4 print iIn python, if the two above threads are run at the same time, thread 1 could output 4, or thread 2 output 1. Let's say that within your system the variable had been "symbol." You system could end up executing orders for a symbol from another system. This is because while it appears that in thread 2 the variable i has been created within a block and therefore has local scope, overriding global variable i in thread 1, due to the way Python works, it is actually a global variable. A simple way to solve this problem is to place you system within a function call. Since variables in functions have local scope, you can use the same variable names across different scripts without problems.
def mySysFunc(): ...do you system logic here mySysFunc() #make a call to your functionYou now know the necessary elements for system building. Everything else is just matter of using which tp functions you need. The data functions are:
tp.bid(symbol) tp.ask(symbol) tp.open(symbol, frequency, offset) tp.high(symbol, frequency, offset) tp.low(symbol, frequency, offset) tp.close(symbol, frequency, offset) tp.volume(symbol, frequency, offset)Fairly self-explanatory. Frequency is data frequency. Use 0 for day and any other number for that many minutes. For example, a frequency of 5 for 5-minute frequency. Offset is the number of "periods ago." For example frequency of 0 and offset of 1, in a call to tp.open(), to get yesterday's open. Note that if you use the history functions, open, high, low, close, and volume, with a frequency of 0 and offset of 0, you are essentially requesting the quote. See module tp reference for information on all tp functions. See the system walkthrough for a line-by-line explanation of an actual system. There are many common indicators in the ind module:
sma(sym, freq, offset, periods) # simple moving average bband(sym, freq, offset, periods) # bollinger bandsSee module ind for information on all available indicators.
1: import ind
2: import tp
3:
4: def sma9sys():
5: sym = ‘XYZ’
6: periods = 9
7: size = 100
8: freq=0
9:
10: while not tp.timeToQuit():
11: if not tp.isOrder(sym, 0):
12: last = tp.last()
13: sma = ind.sma(sym, freq, periods, 0)
14: if last > sma and not tp.isPos(sym, 1):
15 tp.order(sym, size, 0)
16: if last < sma and not tp.isPos(sym, -1):
17: tp.order(sym, size*-1, 0)
18: if(!tp.testStep())
19: break
20: tp.sleep(1000)
| line | description |
|---|---|
| 1-2 | Import the required modules for the system. The ind module includes commonly used indicators. See the reference for a complete list. The tp module contains all the functions needed to write systems, such as data retrieval and trading. |
| 4 | Function declaration. Store system in a function to prevent variable name conflicts with other systems. |
| 5-8 | Assign some values to variables so that the characteristics of the system can be easily changed in the future. |
| 10 | While statement for the main loop. Exits when tp.timeToQuit() returns 1. |
| 11 | Check if there are any unfilled orders before proceeding with system logic. For why see isOrder(). |
| 12-13 | Collect 2 values, the last price and the sma 9, which are needed for the system. |
| 14 | Checks if last trade price was above sma 9 which is a buy signal. Notice the call to isPos(), which prevents the trigger from firing twice. |
| 15 | Submits a buy order for the buy signal. |
| 16 | Checks if last trade price was below sma 9 which is a sell signal. |
| 17 | Submits a sell order for the sell signal. | 18-19 | The testStep() call which, when backtesting, advances the backtest and breaks from the main loop when the backtest is over. |
| 20 | Calls tp.sleep() to prevent this system from using all system resources. |
Place all systems you would like to run in the systems directory. You can change this directory in the Options Dialog. Before running systems you must connect to a data and or trading provider. Once connected, choose main menu Command->Start Systems to start running all systems in the systems directory.
It is possible to run systems which use only a data provider or only a trading provider. If you only need one, data or trading, choose SIMULATION as the other provider.
Unattended mode is designed to allow hands-free automated trading. You can set unattended mode in the main Options dialog, by selecting Options->Options from the main menu.
In unattended mode you must set the market open and market close fields in the Options dialog. The ATS will then start your systems at the market open and stop them at the market close. It will also handle the initialization of your trading or data software if necessary.
indFunc(str sym, int freq, int periods, int offset, list params)
params contains the values from the params field in the edit chart item dialog. For indicators which do not use params you can just pass an empty list. For example, the following call:
sma('XYZ', 0, 9, 1, [])
chart.indDefault() in myind.py, as follows:
chart.indDefault("your_indicator_name", myind.your_indicator_function)def my_custom_draw_indicator(any_param1, any_param2, ... and so forth):def my_custom_draw_indicator_plot(int id, str sym, int freq, int start, int end, int periods, list params)
Here is a description of the parameters:
| id | The unique indicator id. You must pass this value back to TP when drawing so that it knows which indicator you are drawing |
| sym | the indicator's symbol |
| freq | the frequency of the chart on which the indicator is drawn |
| start | the start of the chart, in periods ago |
| end | the end of the chart, in periods ago |
| periods | the periods of the indicator |
| params | A list of params. These are the same parameters entered in the edit item dialog on a chart. Many indicators do not use this, so it will be empty and can be ignored. |
def my_custom_draw_indicator_info(int id, str sym, int freq, int start, int end, int periods, list params, int mousePos)
Parameter descriptions:
| id | the unique id for this indicator |
| sym | the indicator symbol |
| freq | the frequency of the chart on which the indicator is drawn |
| start | the start of the charts, in periods ago |
| end | the end of the chart, in periods ago |
| periods | the periods of the indicator |
| params | a list of parameters |
| mousePos | The position of the mouse over the chart. For example, if the frequency is 0 (daily) and mousePos is 4, the mouse is over the bar that is 4 days ago. |
indCustom(str name, object plotFunc, object infoFunc)
This module contains functions for custom-draw indicators.
Many functions have the parameter itemId of type int. This is the id of the indicator on the chart. When the indicator draw function is called this id is passed to it. You must then pass this id back with the functions listed below so the application knows for which indicator to perform the requested draw action.
| systemPath | path and name of system to be tested |
| symbol | symbol to be tested |
| freq | data frequency |
| start | test start place |
| end | test end place |
True if there is at least one bar in memory for given symbol and frequency, otherwise False.
Returns 1 if there is an open order for symbol, 0 if not. Type can be 1 to signify a long position, -1 for short position, or 0 for any (long or short) position.
Checking whether or not there is an open order before proceeding with system logic is important. Imagine you have a system which has just given a signal to go long. An order for 100 shares is submitted, but only 25 is filled. The signal then gives a short signal. Assuming the previous order has filled, an order to sell 200 shares is sent, so that after filling you will have a short position of 100. However, since only 25 shares of the long order were filled, you end up with a short position of 175 shares. Now, if the short position produce a loss, it will be almost twice as expected.
t = time.localtime() hour = t[3] if(hour >= 10 or tp.testing()) ...
periods is the number of periods to wait for. tries is the number of waits to make. intervals is the number of seconds of each wait.
range() within indicators.def sma(sym, freq, periods, offset, params): rval = 0 for i in range(offset+periods-1, offset-1, -1): rval = rval + tp.close(sym, freq, i) rval = rval / periods return rvalThe code above shows the code for the sma calculation. The only thing you need to pay special attention to is the
for statement. Note specifically the somewhat confusing use of the range function:
range(offset+periods-1, offset, -1)Understanding this line is necessary to properly program your indicators. Notice first of all that the range step is -1. This is because you should draw your indicator in order from oldest period to newest period. This has to do with how the ATS automatically requests data. If you have 9 days of data to request, requesting day 9 first, then 8 to 1, will result in one data request. However requesting day 1 first, through 9, will result in 9 request, and will slow performance. Note that this performance hit only occurs while the data is being request, after it has been requested there performance is the same. Next look at the ending number, offset-1. You want to end on offset. For example, if the offset is 1, 1 days ago, you want the sma calculation to end 1 day ago - which means that day must be included. If you just put offset, rather than offset-1, the range loops will stop at offset+1, and offset will not be included. Finally, the most confusing part, the start element of the range function: offset+periods+1. An sma, of say 5, with an offset of 1, means 5 days of data, ending yesterday, including yester day in the calculation. Here are some days:
Using TA-Lib indicators requires some familiarity with TA-Lib, see ta-lib.org
A call to a TA-Lib indicator takes the following form:
tp.taind(indicator_name, symbol, destination/exchange, frequency, offset, params)
All indicator names available can be seen here.
destination/exchange is not required when trading stocks and an empty string can be used. If you trade futures with IB howerver you will need to enter the exchange.
frequency is bar frequency. See section explaining Frequency.
params is all parameters for the indicator, seperated by space if more than one. For example simple moving average, which has a ta-lib name of SMA, has only one parameter, the number of periods.
The return value of an indicator is a list. Most indicators, such as SMA, only have one return value so it is a single item list. Here is a call to a 9 day simple moving average:
sma = (tp.taind('SMA', 'XYZ', "", 0, 1, '9'))[0]
The same call but in two lines for easier reading:
rval = tp.taind('SMA', 'XYZ, "", 0, 1, '9')
sma = rval[0]
Here is a call to the bollinger bands indicator, which has 4 parameters (inputs) produces 3 outputs:
rval = tp.taind('BBAND', 'XYZ', "", 0, 1, '5 2 2 0')
upperBand = rval[0]
midBand = rval[1]
lowerBand = rval[2]
The best way to learn about what parameters (inputs) an indicator has is to open a chart and then attempt to add an item. The Add Item dialog contains a list of all TA-indicators and upon selecting one all input information is displayed. Here it is for BBAND:

To learn about the return value (outputs) for a TA-Lib indicator add the item to the chart. The information for each output will be displayed on a seperate line in he top left the chart, allowing you to see how many outputs there are as well as what each output is for, such as upper or lower band:

| -4 | tick data |
| -3 | yearly |
| -2 | monthly |
| -1 | weekly |
| 0 | daily |
| 1 and above | Minute frequency. For example 1 = 1 minute, 5 = 5 minute, 60 = 1 hour. |
Copyright © 2007 Henwood Software Corp.
TradeProgrammerTM is a trademark of Henwood Software Corp.