The previous section described how a typical ``elementary'' trading strategy would be represented in TREE. That style is convenient for describing a single strategy working in a single market. This section explains how composite trading systems can be built from collections of elementary strategies.
The simplest form of composite system just runs multiple elementary strategies in parallel, with no interaction between the strategies. The member strategies could be the same strategy (same schematic) applied to different markets, perhaps with slightly different parameters for each market. Or they could be quite different strategies applied to the same or different markets; or any combination. This is easily accomplished in TREE by collecting an instance of each required strategy schematic as a block in a larger schematic. The individual strategy blocks produce status-report events about their positions and profits/losses. From these reports, the outer schematic can produce combined statistics across all the strategies or interesting subsets of them.
Another interesting thing to do with an elementary trading strategy is to
filter its actions in some way. For example, if we have a strategy that
tends to produce runs of winning trades alternating with runs of losing
trades, we might try to improve the results by adding a rule like ``after
two consecutive losers, don't take any more trades until a winning trade has
completed''. There is a bit of fuzzy thinking in this --- if we aren't
taking the trades, how do we know when one was a winner? To eliminate this
logical difficulty, we must distinguish between ``phantom'' trades that the
elementary strategy thinks it is taking, and ``real'' trades that the
filtered strategy is really taking. In TREE, we handle this by using two
separate TradeExec blocks. The one inside the elementary strategy
represents the ``phantom'' trade series; the reports it returns to the
elementary trader look just like they would if the trades were really being
taken. Then we attach an order-filter block that receives both the
elementary strategy's trade order series and the status reports of its
TradeExec. The filter decides whether or not to pass each trade order
on to a second TradeExec, which represents the ``real'' trade series.
We can compare the statistical results from the two TradeExecs to see
how helpful the filtering rule really is.
To make this scheme work in live trading, of course only the second
TradeExec should actually present its orders to the user for execution.
This is easily accomplished by having a ``keep quiet'' parameter for
TradeExecs; a TradeExec that's been turned off in this way doesn't
display orders even in live trading, but simply estimates trade results
based on the incoming data feed, using the same rules as for historical
testing.
The elementary strategy need have no idea that it is being monitored.
It's interesting to note that no TREE trading strategy ever knows for sure
whether its trade orders are really being taken; that fact is hidden by the
TradeExec block.
The order filter doesn't have to just copy or ignore orders from the inner
trader; it could alter the orders or insert orders of its own. For example,
we could build a filter that doubles the amount traded when the underlying
strategy seems to be in a winning run. We might want the filter to turn on
or off while the inner trader has open positions, rather than always waiting
for the end of a trade. In that case the filter would need to insert orders
of its own to get into or out of an open position, whenever it decides to
exit earlier than the inner trader or join a trade that the inner trader has
already started. We might also build filters that use external data to make
their decisions, not just the phantom trade results from the inner
TradeExec. Any number of different filtering rules can be supported by
writing suitable filter blocks.
We can put together these concepts to build a composite ``asset allocation'' trading system. Such a system contains an assortment of component traders, representing one or more elementary strategies operating in one or more markets. But the component traders are only performing phantom trades. For each component we attach an order filter that has an external control input, which tells it whether or not to follow the component's trades, and how many contracts or shares to trade for each one traded by the component. Finally, we create an asset management block that watches the (phantom) results of the component traders, and periodically sends control messages to each of the order filters. The asset manager turns off the filters for traders that are doing poorly, and turns on the filters for those that are doing well. Those that are doing really well get to trade more contracts than those that are not.
As always, the details of the asset management algorithm are embodied in user-modifiable blocks, not buried in some unreachable part of the system machinery. It is easy to experiment with different asset management methods.
Using methods like these, we can build up extremely complex trading systems in a step-by-step fashion. Each component of the asset allocation system just described can be understood, tested, and improved in isolation. Without hierarchical structure to help manage complexity, one could easily be overwhelmed by the task of building such a system.
Copyright 1997, Structured Software Systems, Inc.