TradeProgrammerTM Manual

Copyright © Henwood Software Corp.

Last Update 9-2-08




Table of Contents

System Requirements

Main Menu

Backtest Dialog


Field descriptions:
startthe number of days ago the backtest will start
endthe number of days ago the backtest will end
frequencyfrequency of data that will be used for backtest. frequency codes
systemsContains 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.
symbolsEach symbol in this box will be tested against each system in the systems field.
specify symbolsCheck this box if you would like to type each symbol in the symbols field.
use all symbols in databaseSelect this option if you would like to test every symbol, of the specified frequency, in the data directory against the given systems.
rangeSelect 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.

Backtest Results Window

datedate and time that backtest ended
systemname of system tested
symbolsymbol returned by tp.getSym() during backtest
profit/losstotal profit/loss
optimizationoptimization value for test, see Optimization
startperiods ago test started
endperiods ago test ended
frequencydata frequency returned by tp.getFreq() during test
order counttotal 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.

Chart Window

Right clicking anywhere on the chart produces a pop-up menu:
add itemOpens the Add Item Dialog for adding a new item to the chart.
add graphCreates a new graph at the bottom of the chart.
optionsOpen options dialog for this chart.
clearDelete all items from chart.
edit indicatorbrings up sub-menu of all items on chart. Select one to edit it.
delete indicatorbrings up sub-menu of all items on chart. Select one to delete it.

Other controls:
Close and Save Button***REMOVED***
Quick Symbol EntryThe 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 graphSelects item or graph. Selected item or graph turns bright green.
escape keyDeselect selected item or graph, if any.
del keyDeletes selected item or graph, if any. If a graph is deleted all items it contains will also be deleted.
middle mouse button over itemBrings up edit box for clicked item.
page upincreases size of selected graph by 10%
left arrow keychange primary symbol to proceeding symbol in data directory
right arrow keychange primary symbol to next symbol in data directory
up arrow keysave current chart and load chart proceeding chart in chart directory
down arrow keysave current chart and load next chart in chart directory

Connection Settings Dialog

BrokerageSelect desired trade service provider, and enter username as password.
DataSelect desired data service provider, and enter username as password.
TradeProgrammerEnter the email address and password for your TradeProgrammer account.
connect on startupIf checked the application will automatically connect to selected trade and/or data provider when started.
save passwordsAll passwords will be saved, and reloaded the next time you open the Connection Settings Dialog.

If you only need one API, trade or data, simply select SIMULATION for the other. SIMULATION has no effect if selected as a data API (all request for history will return -1, causing a system to hang.) You can however generate data manually by using the debug quote edit feature (from main menu select Debug->Quote Edit). If selected as a trade API, it will instantly fill any orders placed, at the requested limit price, or 0 is no price is given.

Database Settings Dialog

Field descriptions.
dailythe starting date for daily data stored int database
intradaythe starting date for intraday data stored in database
update bar datadownload all missing historical and intraday data in database
tickthe starting date for tick data stored in database
update tick datadownload all missing tick data in database
exchangeUse 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 filesUpdate 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 folderOpens data directory, allowing you to view all symbol files stored there.

Messages Windows

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.

Options Dialog

offline modeIf 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 scriptIf checked, two scripts can use the same symbol without conflict. *
use test ordersWhen checked, all orders will not be sent to the API but will be stored in application.
market open hour/minuteThe time when your trading begins. **
market close hour/minuteThe time when your trading ends. **
data directorythe directory where your stock data is to be stored
system directoryAll systems in this directory are run when choosing Start Systems from the main menu
max messagesthe maximum number of messages you would like stored in Message Window 1 or 2
trim dataremoves data before and after market open and close you specify, must restart app after checking to take effect

*For example, a call to tp.isPos('XYZ') from script A will return false, even if there is a position of 500 shares, created by an order from script B. All things related to symbols: data requests, orders, positions, profit/loss, are effected. If not checked two different systems using the same symbol will interfere with each other. For most users that should not be a problem, since the average use would choose the best system for an individual symbol, rather than trading the same symbol in multiple systems.

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.

Order Status Window

Displays information on all place orders.

symbolorder symbol
sizeorder size, positive for buy order, negative for sell orders
priceorder price
stateOrder State. Possible values: open, filled.
destinationorder ECN or exchange destination
idorder id
systemthe system that sent this order
timetime order was sent
optoptimization value for this order. See Optimization.

You can sort any of the columns by clicking the column label.

Order Submit Dialog

symbolorder symbol
sizeOrder size. Use positive value for buy order, negative value for sell order.
priceOrder limit price. Set to 0 for market order.
IsTestCheck this box if you want the order to be a test order.

Positions Window

Shows open position information.
systemSystem which generated the position.
symbolposition symbol
sizePosition size. Positive if long position, negative if short position.
test1 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.
optOptimization value which this position applies to. See Optimization.

Quote Edit Dialog

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.

Scan Results Window

scriptthe script used for the scan
symbolthe symbol used for the scan
resultthe result produced by the scan by calling tp.scanResult(str)

You can sort any of the columns by clicking the column label.

Scanner Dialog

symbolsthe symbols that the given scripts will be scanned against
use all symbols in databaseCheck this box to scan again all symbols on disk.
scriptsThe scan scripts. Each will be run against every symbol in symbols. Click browse to open scans directory and select a scan.
frequencyFrequency of data to be used in the scan.

System Status Window

This window displays all systems currently running, along with status. Possible states are:
okThe system is running fine.
not respondingTradeProgrammer 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.
exitedThe 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.
errorsthe script has exited due to an error

Right clicking a line containing a script status brings up the following popup menu:
stopFlags 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.

Trade Statistics Window

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:
systemsystem name
symbolsymbol
total pltotal realized and unrealized profit/loss*
realized plrealized profit/loss
optOptimization value for this position

Activation and Authentication

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.

Backtesting

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 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'

Charting

To open a new chart window, from the main menu click Tools->Chart. Right clicking on the chart will produce a pop-up menu, where all aspects of the chart, such as indicators, frequency, and time span, can be controlled. You can place any number of indicators on a graph, and a chart may contain any number of graphs.

If you have not connected to a data API, the chart will search the disk(your database) for the required symbols. If TradeProgrammer is connected data will be requested from the API.

While both historical and real-time intraday charting is supported, you are limited by the capabilities of the API you use. Some only support historical data. Others support intraday but have limited frequencies or limit on the time span of data requested.

Check the Chart Window reference and the Charting Tutorial for more information.

Scanning

To start a scan click Tools->Scanner from the main menu. Read section Scanner Dlg for information on using the Scan Dlg. Once you have entered the scan information, click ok. Open the Scan Results Wnd from main menu Windows->Scan Results, to view scan results.

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

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-13These 4 lines enable optimization when testing, and basically turn off optimization by setting only one opt value, 9, for live trading.
9Fills an array with all the optimization values that will be used.
11A 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.
12Sets 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.
13Adjust the periods of the sma used. Affects call to ind.sma() on line 17.

Creating Systems

Almost all systems you program will probably have a main loop. Within your main loop you must check to see if the application want's your system to stop execution. To do so, call tp.timeToQuit(). This function returns true when your script should stop execution such as when the user has requested that all scripts stop.

Here is an example:
import tp

while not tp.timeToQuit():
	#do system logic

If 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())
		break

An 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 1
int 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 1
i = 1
print i
thread 2
if(contrition=True)
	i = 4
	print i
In 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 function

You 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 bands
See module ind for information on all available indicators.

System Walkthrough

This section offers a line-by line analysis of a trading system based on a 9 day simple moving average to help you learn the elements of system creation. The system goes long when the closing price is above the sma 9 and goes short when the closing price is below the 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:		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)
tr>
linedescription
1-2Import 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.
4Function declaration. Store system in a function to prevent variable name conflicts with other systems.
5-8Assign some values to variables so that the characteristics of the system can be easily changed in the future.
10While statement for the main loop. Exits when tp.timeToQuit() returns 1.
11Check if there are any unfilled orders before proceeding with system logic. For why see isOrder().
12-13Collect 2 values, the last price and the sma 9, which are needed for the system.
14Checks 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.
15Submits a buy order for the buy signal.
16Checks if last trade price was below sma 9 which is a sell signal.
17Submits a sell order for the sell signal.
18-19The testStep() call which, when backtesting, advances the backtest and breaks from the main loop when the backtest is over.
20Calls tp.sleep() to prevent this system from using all system resources.

Running Your Systems

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

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.

Creating Indicators

The first step in creating an indicator is to decide if your indicator function can use the standard indicator format:

indFunc(str sym, int freq, int periods, int offset, list params)

Most indicator can use this format, since most indicators mostly just rely on the symbol, frequency, periods, and offset. Offset is the number of periods ago. 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, [])

returns the sma 9 for XYZ, 1 day ago.

Most indicators can use the standard format. If the standard format is used your indicator will be drawn on charts in the form of a line plot. All you need to do is place a call to chart.indDefault() in myind.py, as follows:
chart.indDefault("your_indicator_name", myind.your_indicator_function)
The first parameter the name you would like to call your indicator, in this case "sma," and the second parameter is a pointer to your indicator function. The function is plotted as a line. That is to say, a the value is calculated for each place on the chart, and a line is drawn from each value to the next. The file myind.py can be found the /tradeprogrammer/py/ directory.

If your indicator function can't fit into the standard form or your indicator can't be drawn as a line you will have to make a custom draw indicator. The indicator function declaration can now take any form you like, since the TradeProgrammer application will not be calling it:
def my_custom_draw_indicator(any_param1, any_param2, ... and so forth):

To actually draw the indicator, you will have to make two functions in myind_draw.py which the TP app will call when it is time to draw. The first function is responsible for drawing the indicator and has the following form:
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:
idThe unique indicator id. You must pass this value back to TP when drawing so that it knows which indicator you are drawing
symthe indicator's symbol
freqthe frequency of the chart on which the indicator is drawn
startthe start of the chart, in periods ago
endthe end of the chart, in periods ago
periodsthe periods of the indicator
paramsA 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.

The second function is responsible for drawing the indicator info. The info is whatever text you would like to display for the indicator based on the current position of the user's mouse cursor on the chart. For example, the bollinger band indicator prints the value of the upper and lower band along with the symbol for which the bands are being drawn and the name of the indicator "bollinger bands." Here is the form of the info-drawing function:
def my_custom_draw_indicator_info(int id, str sym, int freq, int start, int end, int periods, list params, int mousePos)

Parameter descriptions:
idthe unique id for this indicator
symthe indicator symbol
freqthe frequency of the chart on which the indicator is drawn
startthe start of the charts, in periods ago
endthe end of the chart, in periods ago
periodsthe periods of the indicator
paramsa list of parameters
mousePosThe 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.

Once you have created your draw and info functions you must notify the application of you indicator with the chart.indCustom() function, which takes the following form:

indCustom(str name, object plotFunc, object infoFunc)

Where name is the name you choose for you indicator, plotFund is the drawing function, and infoFunc is the info-drawing function. This function call is placed in /tradeprogrammer/py/myind_draw.py. Place it at the end of the file after all your custom draw functions.

Module chart

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.

box(int itemId, double top, double left, double btm, double right, double, bool fill)

Top, left, btm, right, are the bounds of the box. Pass True to fill if the box is to be drawn filled.

color(int itemId, int r, int g, int b)

Set draw color. r, g, b are the red, green, and blue elements of the color. Valid values for r, g, b are 0 to 255.

info(int itemId, str infoText)

Displays the info for an indicator which should the calculated value(s) of the indicator in any form you like. For example, the info function for the sma indicator displays the sma value, while the info function for candlestick displays the open, high, low, and close price.

indCustom(str indName, PyObject plotFunc, PyObject infoFunc)

If you don't want the application to draw you indicator for you must create your own custom drawing function and then notify the application by a call to indCustom. This is necessary for many specialized or complicated indicators which require extra steps for drawing. See Creating indicators for more information.

indName is the name of your indicator. plotFunc is the function of your indicator function and infoFunc is the function of the draw function for you indicator.

indDefault(str indName, PyObject indFunc)

Call this function when you want the TradeProgrammer application to handle the drawing of your indicator. indName is the name you choose for the indicator. indFunc is your indicator function, which must have the following form:
indFunc(str sym, int freq, int periods, int offset, list params)

lineto(int itemId, double x, double y)

Draws a line on chart from last draw position to x, y. x is the "place" on the chart. Place 0 is the left most bar. If the chart has 30 bars then place 30 is the right most bar. Y is the

moveto(int itemId, double x, double y)

Moves drawing position to x, y. x is place and y is price.

Module tp

All data requesting functions - open, high, low, close, volume, bid, ask, last, bidsize, lastsize, and others - will wait forever for the data become available. This is because returning a "nothing" value, such as 0 or -1, might make your system think the actual price is that number. The only way to avoid this is to wait forever for data. (If you determine a system is stuck waiting for data, you can stop in manually, in the normal way, by opening the System Status window, right-clicking the system, and choosing "stop.") This is possible because the ATS checks tp.timeToQuit() while the data function is waiting for data.)

barDate(str sym, int freq, int offset

Returns a Python datetime object containing time for requested bar.

backtest(str systemPath, str symbol, int frequency, int start, int end)

Causes application to begin backtesting using given parameters.
systemPathpath and name of system to be tested
symbolsymbol to be tested
freqdata frequency
starttest start place
endtest end place

float avgPosPrice(str sym)

Returns average position price for symbol, 0 if no position.

int barPos(str sym, int freq, int year, int month, int day, int hour, int min)

Returns the offset for bar which has a matching date.

int closePos(str symbol)

Closes position for given symbol by submitting a market order. If the 'use test orders' option is checked, closePos will work only for test positions, otherwise closePos will work only for real positions.

str getDataApiName()

Returns a string containing the name of the current data provider ATS is connected,to if any, otherwise returns "".

int getPeriods(str sym, int frequency)

Returns the number of bars in memory for given symbol and frequency.

list getOrders(sym)

Returns all orders, filled, canceled, and live, in a list. Each row in list contains another list which represents an order, in the following format:
[price, size, year, month, day, hour, minute, second]
Price is the order fill price if order has been filled or limit price if order not filled. Size is order size, positive for buy orders and negative for sell orders. Year, day, hour, minute, and second are the time at which the order was submitted.

int getFreq()

Returns system's default frequency. Set default frequency with tp.setFreq() function. If default frequency has not been set getFreq() returns 0 (daily frequency.)

str getSym()

Returns system's default symbol. Set default symbol with tp.setSym() function. If default symbol has not been set returns "".

double hist(str symbol, str exchange, int frequency, int offset, int field)

Return historical data value. Field can be one of the following values: tpdef.OPEN, tpdef.HIGH, tpdef.LOW, tpdef.CLOSE, tpdef.VOLUME

isHist(str symbol, int frequency)

Returns True if there is at least one bar in memory for given symbol and frequency, otherwise False.

bool isOrder(str symbol, int type)

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.

bool isPos(str symbol, int type)

Returns 1 if you account has any position, long or short, for symbol. Type can be 1 to signify a long position, -1 for short position, or 0 for any (long or short) position.

order(str symbol, int size, float price)

Places an order for symbol. Size is number of shares to buy/sell. Use positive number for buy order, negative for sell order. Price is limit price. Use 0 for price if market order.

list ordersAtPlace(str symbol, int frequency, int offset, bool test)

Returns all orders that would fit on a chart bar, for given symbol, frequency, and offset. Pass 1 for test if you want to retrieve test orders, 0 for real orders. This function is used in the 'orders' indicator to draw orders on a chart.

double quote(str symbol, str exchange, int field)

Returns quote field value. Field can be tp.BID, tp.ASK, tp.LAST, tp.BIDSIZE, tp.ASKSIZE, tp.LASTSIZE, tp.OPEN, tp.HIGH, tp.LOW, or tp.VOLUME.

rpl(str symbol)

Returns realized profit/loss for symbol.

scanResult(str resultText)

Adds a "result" to the Scan Results Window. A result is anything you like: "yes", "0", "1", etc.

setFreq(int frequency)

sets the default frequency for the system.

setOpt(str optvalue)

Sets the optimization value for current script. All following calls to order(), isPos(), isOrder(), will use this optimization value. See Optimization.

setSym(str symbol)

Sets the default symbol for the system.

setTestOrders()

After calling this function, all orders for the system will be treated as test orders. Use this function rather than setting the "Use Test Orders" option from the Options Dialog when you need to trade both live and test systems at the same time. Simply place a call to setTestOrders() at the beginning of your test systems.

sleep(int seconds)

Pauses execution of system for given number of seconds. This function should be used instead of built-in module tp.sleep() since it is ignored during manual backtests. If you leave a time.sleep() call in you main loop during a backtest it will greatly slow test time.

startHist(str symbol, int frequency, int periods, int offset)

Requests historical data. Periods is number of periods to request. Offset if the number of periods ago the request starts. For example, a request for 10 periods, frequency of 0, offset of 5 is a request for the days between 15 and 5 days ago. The same request, with offset of 0, would simply request between 10 days ago and today.

text(str s)

Outputs s to Message Window 2.

bool testing()

Returns 1 if backtest is in progress, 0 otherwise. Use this to bypass code that is not needed during backtesting, such as time and optimization. For example, if you have a system which only starts at 10 am, that will make it impossible to backtest, except after 10 am. Use tp.testing() to bypass the time requirement when backtesting, as follows:
t = time.localtime()
hour = t[3]
if(hour >= 10 or tp.testing())
	...

testStep()

Advances a manual backtest by one period. Call this function from within your main loop. The function call has no effect when running your system live, allowing you to switch between manual backtesting and real trading without code modification.

timeToQuit()

Returns True if the application user has requested that the script stop execution, False if not.

upl(str symbol)

Returns unrealized profit/loss for symbol.

unloadHist(str symbol, int freq)

Deletes all bars in memory for given symbol and frequency. TradeProgrammer already takes care of creating and deleting bars as needed, so you would probably not have to use this function except in special situations.

volume(str symbol, int frequency, int offset)

Returns volume for given symbol, frequency, and offset.

waitHist(str symbol, int frequency, int periods, int tries, int intervals)

Waits for specified historical data to be available. 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.

Technical note: Proper use of range() within indicators.

Assuming your indicator can use the standard indicator function format, lets examine the sma indicator so you will understand how to make your own: (the sma indicator can be found in /py/ind.py in the TradeProgrammer installation directory.)
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 rval
The 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:
6, 5, 4, 3, 2, 1, 0
0 is today, we are not concerned with it. Now, we need 5 days of data. Starting a 1 and including 1, count backwards 5 times. You reach 5. Therefore we need days 5, 4, 3, 2, and 1 for the sma 5. In the range function, offset=1, periods=5. 1+5-1 = 5, the correct number of days. It is easy to simple add offset and periods, which would produce 6, which would add one extra day to the calculation, throwing it off.

Using TA-Lib Indicators

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:

Frequency

Data frequency, such as daily, monthly, hourly, etc. Integer value is used for each frequency, see table below.
-4tick data
-3yearly
-2monthly
-1weekly
0daily
1 and aboveMinute 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.