Automated System Development
In
the July 2006 issue of Futures magazine (ref. 1), I read an interesting article
by Mike Barna. He described a computer program he developed that
automatically generates trading systems using a genetic programming algorithm.
Very few details about how the algorithm works were provided, and the article
was laced with technical jargon. However, the basic idea was clear. He was using
a computer algorithm to construct trading rules and combine them in different
ways in order to find the set of trading rules that worked best. It was
optimization of the trading system development process, rather than
optimization of trading systems per se. He followed that article with one the
next month that provided an example of a trading system produced by this
process (ref. 2).
When
I accessed Mr. Barna’s web site, I was interested to find that he was getting
ready to sell a commercial version of his software. My interest quickly faded
when I learned he was planning to sell the program for $60,000 per copy. My
first thought was “I wonder if I could do something like that myself”. So, this
month, I’m presenting my own simplified version of automated system generation.
On
his web site, it says that his product is the result of years of hard work. My
version took about three days, so you can probably guess it’s not as
complicated or sophisticated as Mr. Barna’s program. On the other hand, I’m not
only disclosing how I did it, I’m giving you the code for free. As I suspect
you’ll agree, it’s surprising how much one can actually accomplish with some
fairly simple algorithms.
Price
Pattern Rules
In
principle, any type of indicator or trading logic could be included in the
trading system generator I’ve developed. However, to keep things fairly simple
for the purposes of this newsletter, I decided to restrict the rules of the
generated trading systems to price patterns. Each entry rule of a generated trading
system will have the following form:
P1[N1] Ineq P2[N2]
where
P1 and P2 are prices (open, high, low, or close), N1 and N2 are the number of
bars to look back (e.g., Close[2] is the close two bars ago), and Ineq is an
inequality, either <= or >=. Examples of rules include the following:
Close <= Close[2]
Low[2] <= High[10]
High[3] >= Close[4]
and
so on. P1, P2, N1, N2, and Ineq are all variables to be determined by the
system generation process.
N1
and N2 will be restricted to the range 0 – 20. Also, the number of rules,
NRules, will be a variable with values ranging from one to 10. A trade entry
will be triggered if all the rules are true. In that case, the entry will be
taken at the open of the next bar. The trade direction will be set beforehand,
so that the system will be generating systems that are either all long or all
short trades. This is done because some possible sets of rules will have no
obvious logical inverse, so it wouldn’t be possible to simply reverse the logic
for long trades to get a short entry condition, or vice-versa. To obtain
trading logic for both long and short trades, the system can be run twice, once
for long trades and the second time for short trades.
Trades
will be exited at the market after a fixed number of bars, NX, which will range
from one and 20.
Finding
the Rules
The
key to this process is finding candidate trading systems. A system can consist
of between one and 10 rules of the form shown above. We enter at market if all
the rules are true, and we exit a certain number of bars later. If this were
coded as a traditional TradeStation system, we’d have no practical way to
optimize it because TradeStation uses a direct optimization method in which all
possible combinations are considered. In this case, with a maximum of 10 rules,
there are more than a trillion combinations, not even considering the parameter
values.
A
different method, such as the genetic algorithm that Mr. Barna used, is needed.
However, rather than program a genetic algorithm for this article, I decided to
use a “poor man’s” genetic algorithm. At each step of the optimization, the
values for each variable (P1, P2, N1, N2, Ineq, NRules, and NX) will be chosen
randomly. There will be multiple values of P1, P2, N1, N2, and Ineq depending
on the number of rules, NRules.
Each
step of the optimization generates a different trading system as the variables
are randomly selected. If the performance results of the system meet the
requirements entered by the user, the generated system will be written to a
file in EasyLanguage code.
Putting
it All Together
The
code for the AutoSystemGen system is shown below.
{
AutoSystemGen
This system is designed to automatically generate profitable
trading systems based on price patterns. When the system is
optimized, it randomly generates price patterns and writes
Easylanguage trading system code for each system that meets
specified performance criteria. After the optimization is
complete, the code for each successful system can be found
in the text file specified in the function WriteSystems.
Mike Bryant
Breakout Futures
www.BreakoutFutures.com
mrb@BreakoutFutures.com
Copyright 2007 Breakout Futures
}
Inputs:
OptStep (0), { Optimization step; optimize this
var }
TradeDir (1), { +1 -> long trades; -1 -> short
trades }
reqNetProf(0), { minimum required net profit }
reqMaxDD (0), { required max drawdown limit }
reqNTrades(0), { required minimum number of trades }
reqPerWins(0), { required minimum percentage of wins }
reqProfFac(0); { required minimum profit factor
}
Var:
NRules (0), { Number of rules/patterns to use }
NBarExit (0), { Number of bars to hold trades }
ii (0), { Loop counter }
ProfFac (0), { profit factor }
FuncRet (false),{ function return variable }
EntNext (false);{ logical flag for entering next bar }
Array:
PriceL[10](0), { 0 - 3 for O, H, L, C for left-hand side }
PriceR[10](0), { 0 - 3 for O, H, L, C for right-hand side }
InEq[10] (0), { 0 - 1 for >=, <= }
DaysL[10] (0), { days back for left-hand price }
DaysR[10] (0); { days back for right-hand price }
{
Randomly select values for the variables that define the rules }
If
BarNumber = 1 then
FuncRet = GetPatVars(PriceL, PriceR, InEq, DaysL, DaysR, NRules, NBarExit);
{
Evaluate the price patterns/rules to determine if a trade should be placed }
EntNext
= true;
For
ii = 0 to NRules - 1 Begin
EntNext = EntNext and EvalPattern(ii, PriceL, PriceR, InEq, DaysL, DaysR);
End;
{
Place the trade, either long or short }
If
EntNext then Begin
If TradeDir > 0 then
Buy next bar at market
else if TradeDir < 0 then
Sell short next bar at market;
End;
{
Exit the open trade NBarExit bars after entry }
If
BarsSinceEntry = NBarExit then Begin
If MarketPosition > 0 then
Sell next bar at market
else if MarketPosition < 0 then
Buy to cover next bar at market;
End;
{
Write out the trading system code if the system is successful }
If
AbsValue(GrossLoss) > 0 then
ProfFac = GrossProfit/AbsValue(GrossLoss)
else
ProfFac = 100;
If
LastBarOnChart and NetProfit >= reqNetProf and
AbsValue(MaxIDDrawDown) <= AbsValue(reqMaxDD) and
TotalTrades >= reqNTrades and
PercentProfit >= reqPerWins and
ProfFac >= reqProfFac then
FuncRet = WriteSystem(OptStep, TradeDir, PriceL, PriceR, InEq, DaysL, DaysR,
NRules, NBarExit,
"C:\AutoSysGen-Output1.txt");
If
you look at the system inputs, the first input is called OptStep. To run this
system, you optimize it in TradeStation by varying this input from 1 to some
large number, such as 10,000, in steps of 1. This will cause the system to
generate, for example, 10,000 different trading systems. The ones that meet the
specified performance criteria are written to the file shown as an input to the
WriteSystem function (C:\AutoSysGen-Output1.txt). The performance
criteria are specified via the system inputs (reqNetProfit, reqMaxDD, etc.).
Most
of the hard work is performed by the functions that the system calls. The
function GetPatVars randomly selects the
values for the variables that determine the trading rules. To determine whether
or not a trade entry will occur on the next bar, the price pattern rules are
evaluated by the function EvalPattern. Finally, if the system meets the performance criteria,
the corresponding EasyLanguage code is generated and written out to a text file
by the function WriteSystem. The code for the AutoSystemGen system and all the
functions is available on my
download page.
Example
As
an example, consider the 30-year treasury bond futures market (symbol @US.P in
TradeStation 8.2). I optimized the AutoSystemGen system over the past 20 years
of t-bond prices with the OptStep input optimized from 1 to 10000. This means
the system evaluated 10,000 different trading systems. I ran the optimization
twice, once for long trades and once for short trades. For performance
criteria, I chose the following requirements: net profit of at least $30,000,
worst-case drawdown no more than $7500, at least 200 trades, percent profitable
of at least 50%, and profit factor of at least 1.2. On my dual core process
computer running Vista, it took approximately 10 minutes to run each
optimization (10,000 systems per optimization).
The
systems generated by this process are shown below. These are the systems written
to the file AutoSysGen-Output1.txt by the WriteSystem function. The first ones
are the long-only systems, followed by a short-only system (the only one that
met the performance criteria).
System
2332, @US.P, 9/17/2007 12:23:00, Long Trades
Net
Profit = 53562.50, Max DD = -7381.25, Num Trades = 250, Percent Wins = 56.80,
Prof factor = 1.631
{
System code starts here... }
Var:
EntNext (false);
EntNext
= Open[2] >= Low[16] and
Low[9] >= Low[3] and
Close[14] <= Low[6] and
High[1] >= Low[3];
If
EntNext then
Buy next bar at market;
If
BarsSinceEntry = 2 then
Sell next bar at market;
{
End system code }
System
5771, @US.P, 9/17/2007 12:27:00, Long Trades
Net
Profit = 42145.00, Max DD = -5733.75, Num Trades = 207, Percent Wins = 57.00, Prof
factor = 1.631
{
System code starts here... }
Var:
EntNext (false);
EntNext
= High[7] >= Low[19] and
Close[20] >= Close[5] and
High[18] >= Low[2] and
High[2] <= Open[6];
If
EntNext then
Buy next bar at market;
If
BarsSinceEntry = 2 then
Sell next bar at market;
{
End system code }
System
7622, @US.P, 9/17/2007 12:29:00, Long Trades
Net
Profit = 59348.75, Max DD = -7222.50, Num Trades = 208, Percent Wins = 60.58,
Prof factor = 1.924
{
System code starts here... }
Var:
EntNext (false);
EntNext
= Low[2] <= High[9] and
Open[11] >= Open[18] and
Close[17] <= Low[18];
If
EntNext then
Buy next bar at market;
If
BarsSinceEntry = 3 then
Sell next bar at market;
{
End system code }
System
7718, @US.P, 9/17/2007 12:29:00, Long Trades
Net
Profit = 35526.25, Max DD = -6936.25, Num Trades = 292, Percent Wins = 56.85,
Prof factor = 1.418
{
System code starts here... }
Var:
EntNext (false);
EntNext
= Close[3] >= High[19] and
High[8] >= Low[9] and
High[6] <= Open[10] and
Low[16] <= High[3];
If
EntNext then
Buy next bar at market;
If
BarsSinceEntry = 1 then
Sell next bar at market;
{
End system code }
System
6160, @US.P, 9/17/2007 12:42:00, Short Trades
Net
Profit = 31277.50, Max DD = -6846.25, Num Trades = 369, Percent Wins = 51.76,
Prof factor = 1.297
{
System code starts here... }
Var:
EntNext (false);
EntNext
= High[9] >= Low[6] and
Close[15] >= High[8] and
High[7] <= Low[20] and
High[6] >= High[7];
If
EntNext then
Sell short next bar at market;
If
BarsSinceEntry = 1 then
Buy to cover next bar at market;
{
End system code }
The
listing for each system includes the system number (corresponding to the
OptStep input), market symbol, current date, and whether the system is
long-only or short-only. The next line contains a few summary performance
statistics to help in evaluating each system. Finally, the system code is
shown. To evaluate the systems in TradeStation, the code between the two comment
lines ({ …}) can be copied and pasted into a strategy in TradeStation, then run
in the chart window.
For
example, I copied the first system shown above (#2332) to TradeStation and
saved it as a strategy. When I ran this on the @US.P chart, I obtained the
following equity curve:
Figure
1. Long-only system for t-bonds, last 20 years, with $15 per trade deducted for
trading costs, generated by system AutoSystemGen.
The
last system in the output file is a for a short-only system (#6160). When saved
in TradeStation as a strategy and applied to the same t-bond chart, the
following equity curve is produced:
Figure
2. Short-only system for t-bonds, last 20 years, with $15 per trade deducted
for trading costs, generated by system AutoSystemGen.
With
a small amount of additional effort, the two systems could be combined into a
single system that generates the long and short trades in the same system.
Comments
Automatically
generating trading systems is an attractive idea. The traditional process of
developing a trading system is extremely time consuming and involves
systematically eliminating many ideas that simply don’t work. Also, we all have
biases about how the markets work, and these biases affect how we develop
trading systems. Hopefully, our biases guide us in the right direction, but
they can also limit the possible systems we might come up with. Rather than
starting with a market bias and a limited set of rules, an automatic system
generator starts with a wide array of possible rules and finds what works
without bias while quickly eliminating what doesn’t.
The
downsides of automatic system generation are (1) it’s not a trivial matter to
develop a good system generator, and (2) it’s an optimization process, with all
the caveats of any trading system optimization. The AutoSystemGen system
described here is a very simple system generator limited to price patterns with
a simple exit rule. However, the process could be expanded to include other
types of indicators and system rules. To make it suitable for day trading, an
exit-on-close rule could be added to each generated system. To find better
systems faster, the random process of assigning values to variables could be
replaced with a genetic algorithm.
It’s
notable that the random selection of values for variables is as effective as it
is. A genetic algorithm starts with a randomly drawn selection of values, but
then modifies the values to “evolve” other sets of values. Randomness is
introduced periodically in genetic algorithms to shake things up and make sure
enough variation is introduced into the search process. For the AutoSystemGen
system, I’ve effectively taken a genetic algorithm and eliminated everything but
the randomness. And the algorithm is still fairly effective.
The
AutoSystemGen system is optimizing both the selection of rules and the
parameter values of the system simultaneously. As with any type of system
optimization, it’s important to test out-of-sample, preferably in real time.
Real-time tracking of the generated systems prior to committing real money is
the best way to be sure the systems are not over-fit to the market. A system
that works well in real time following optimization has the best chance of
holding up well in actual trading.
References
1.
Mike Barna, Building Trading Systems the Automatic Way, Futures, July 2006, pp.
44-46.
2.
Mike Barna, Going for the Gold -- Automatically, Futures, August 2006, pp.
44-47.