Trading and Investment Strategies
There are no real restrictions on the complexity or scope of trading and investment ideas that can be modelled with Time Series API (TSA). Large simulations can be broken into manageable steps, and input/output communication with external systems is not restricted in any way. Numerous performance statistics and reports are available to evaluate strategies as well as help assess and understand risks. New indicators can be implemented quickly and easily to complement the library's native functionality.
Formula code supports buying and selling via the following functions:
- MarketOrder()
- StopOrder()
- LimitOrder()
Orders created by these functions are placed in the strategy object's internal order queue. New orders are generated at the close of each bar for execution at next bar. Order execution is controlled a number of strategy properties that control order size interpretation (cash vs. units), transaction costs, the manner in which costs are allocated (per transaction vs. per unit), and the effect that each order has on the position (pyramiding), etc.
An example of the basic structure of a strategy:
Strategy s;
s.SetBigPointValue(
50.0 );
s.SetPerSideCommission(
2.0 );
s.SetPerSideSlippage(
12.5 );
s.SetOrderSizeAs(NUM_UNITS);
s.SetUsingFractionalUnits(false);
s.SetUsingPyramiding(true);
s.SetLimitExecutionFactor(0.0001);
Double dOrderSize = <...>
Bool bLongEntrySignal = <...>
MarketOrder(
BUY, dOrderSize, bLongEntrySignal,
NEXT_OPEN);
Bool bReduceLongSignal = <...>
Double dReduceBy
= PositionSize() / 2;
Double dLimitPrice = <...>
LimitOrder( REDUCE_LONG, dReduceBy ,
bReduceLongSignal,
dLimitPrice,
NEXT_BAR);
Bool bExitLongSignal = <...>;
Double dStopPrice = <...>
StopOrder(
EXIT_LONG,
bExitLongSignal
,
dStopPrice,
NEXT_BAR);
Strategy Properties
The strategy's SetUsingPyramiding() member controls whether or not new orders are ignored if they increase the initial position. The SetUsingFractionalUnits() member controls whether trading in 'fractional units' is allowed (to accomodate unusually high nominal stock prices for example), and the SetLimitExecutionFactor() member controls by how far traded prices need to move 'beyond' a limit order's 'limit' price before the order is assumed to be filled. The numerous other strategy properties will be discussed in the strategy examples that follow.
Data Access
Data access is performed by the IRecord() function. The IRecord() function returns an object of type Tuple, representing a fixed size collection of uniquely named values. class Tuple derives its name from words such as quituple, sextuple, etc. describing multiples. Individual values can be accessed by name provided the underlying table schema contains corresponding fields named:
Double op = data("open");
Double hi = data("high");
Double lo = data("low");
Double cl = data("close");
Order Flags
LimitOrder( REDUCE_LONG, dReduceBy ,
bReduceLongSignal,
dLimitPrice,
NEXT_BAR);
The time at which each order is executed is controlled by arguments passed directly to order function. Valid flags are NEXT_OPEN, NEXT_BAR, NEXT_CLOSE and THIS_CLOSE. In addition there are flags such as BUY, LONG, REDUCE_LONG, EXIT_LONG etc. that control the effect orders have on the existing position.
Multi Leg Positions
Trades can be composed of any number of trade 'legs', some of which increase the initial position and others which decrease the initial position. A 'trade' is only considered closed out when the position goes 'flat' or 'reverses'. Strategies are thus free to adapt position size to market conditions via 'add on' orders or 'partial exists' without first having to 'close out' an existing trade.
The various available logs and reports (Trade Report, Order Report, Transaction Report etc.)can help analyze what exactly happened at any point during the simulation. Click here for more on this topic:
EXAMPLE A: Trading Seasonal Tendencies
Market analysis often involves identifying cycles. The most basic cycles of course are the daily and yearly cycle. The following short strategy will try to identify clusters of days each year when it it worthwhile buying the stock index. The idea is that the stock market has had a tendency to rise on specific days of select months due to seasonal forces, and that these seasonal forces will continue to excert their influence in future.
What follows is a strategy that trades seasonality in the Dow Jones Industrial Index (INDU). The strategy collects seasonal information from 1910 onward, analyzes the average seasonal changes for every date (in a cummulative manner) and trades based on its findings. If it finds that more than 50 percent of historical price changes corresponding to the next 4 trading days (cluster) were positive, then the strategy goes 'long'. The position is exited after a fixed four days. The strategy does not engage in short trading!
Although the Dow Jones Industrial is an index, the strategy buys and sells it as it would a stock.
Strategy s("seasonal_demo"); //1
s.SetPerSideCommission(0.0); //2
s.SetPerSideSlippage(0.0); //3
s.SetOrderSizeAs(CASH_AMOUNT); //4
s.SetBigPointValue(1.0); //5
s.SetUsingPyramiding(false); //6
s.SetHolidayRegion(db, "NYSE_holidays"); //7
Tuple data = IRecord(db, "indu.index.close.daily", PRICE_MASTER | TIME_MASTER ); //8
DoubleVector close = data("close"); //9
Handle sd = SeasonalData( StrategyDate(), close - close[1], 8000); //11
Bool buySignal = SeasonalPercentUp(sd, ForwardStrategyDate(1)) > 50.0
&& SeasonalPercentUp(sd, ForwardStrategyDate(2)) > 50.0
&& SeasonalPercentUp(sd, ForwardStrategyDate(3)) > 50.0
&& SeasonalPercentUp(sd, ForwardStrategyDate(4)) > 50.0; //12
MarketOrder(LONG, 1000.0, buySignal, THIS_CLOSE); //13 Long Entry
MarketOrder(EXIT_LONG, BarsInTrade() == 4, THIS_CLOSE); //14 Long Exit
s.Log( db, TRADES | DRAWDOWNS ); //15
s.Evaluate(1910, 2005); //16 simulating 95 years of trading
s.PrintEquityGraph( file ); //17
s.PrintPerformanceReport( file ); //18
s.PrintTradeReport( file ); //19
s.PrintDrawdownReport( file ); //20
Strategies all have a similar structure. First, the various strategy properties are defined (//2 to //7), followed by the formula code which expresses the strategy logic itself (//8 to //14). Then the strategy is evaluated by a call to its Evaluate() member (//16) before any of the available reports are printed. Some reports require logs to be written during evaluation, hence the call to the Log() member in //15.
Data Access
Tuple data = IRecord(db, "indu.index.close.daily", PRICE_MASTER | TIME_MASTER ); //8
Records are read via the IRecord() member. There are no restrictions on the number of data streams that can be opened by a strategy and IRecord() can be called any number of times to perform intermarket analysis, calculate spreads and custom indices etc. The strategy does nonetheless need to know which time series corresponds to the security that the strategy intends to buy or sell. This time series is called the PRICE_MASTER! In this case, chosing the PRICE_MASTER is easy as the strategy reads data from just a single table ("indu.index.close.daily") containing the Dow Jone's daily price history.
The strategy also needs to know about scheduling, to determine the timestamp sequence that the simulation uses while interating through the simulation period. This scheduling sequence is called the TIME_MASTER and the same time series can server as both TIME_MASTER and PRICE_MASTER, both of which are defined via flags of the same name to the IRecord() function, as in //8.
Calculating Seasonality
DoubleVector close = data("close"); //9
Handle sd = SeasonalData( StrategyDate(), close - close[1], 8000); //11
Bool buySignal = SeasonalPercentUp(sd, ForwardStrategyDate(1)) > 50 //12
(. . . )
The buy signal is produced by lines //11 and //12. The SeasonalData() function controls an internal component that maintains a database of seasonal price changes, for every 'month/day' pair. The 'month/day' value is taken from the first argument. The price change is passed as second argument, given as close - close[1]. Note the use of operator[] to access yesterday's close. Operator[] is only available for vector types, which is why 'close' was declared as type DoubleVector in //9. The third argument is 8000 and tells the component to accumulate 8000 data items before returning any metrics.
What is unusual about the SeasonalData() function is that it returns a handle to a component. This allows the component's internal database to be shared by various function such as SeasonalPercentUp(), SeasonalCountUP(), SeasonalCount(), SeasonalAverage() rather than having to duplicate the same calculations and data structures for each derived metric!
The SeasonPercentUp() function (//12) simply returns the percent of years for which the price change was positive on the given 'month/day'. The 'month/day' is taken from the date returned by the ForwardStrategyDate(1) function, which returns the date of the next trading day, taking into account weekends and holidays.
Generating Orders
One of the callenges analysts face when working with long timespans, in this case almost a century, is that there can be serious distortions in price due to accumulated inflation. Normally, strategies would trade a fixed number of 'shares' with each trade, but this gives too much weight, in terms of Profit/Loss contribution, to the trades near the end of the period. After all, 10000 dollars are required to buy the dow at 10000 and only 100 dollars to buy it at 100 in 1920.
One way to even out exposure is to trade a fixed dollar amount, and to allow the strategy to trade fractional units. This is achieved in line //4 by:
s.SetOrderSizeAs(CASH_AMOUNT); //4
This causes the 1000.0 in line //13 to be interpreted in 'currency units' rather than 'number of shares'.
MarketOrder(BUY, 1000.0, buySignal, THIS_CLOSE); //13
1000 dollars will buy 10 shares of the Dow in 1920, and only 0.1 shares in the year 2000. For the simulation to encounter no problems, the trading of fractional units must be allowed (default), a strategy property controlled by the SetUsingFractionalUnits() member.
MarketOrder(EXIT_LONG, BarsInTrade() == 4, THIS_CLOSE); //14
The EXIT_LONG flag in line //14 instructs the system that this is an 'exit' order only, and that it should be ingnored if no position exists. Such flags allow some additional logic to be passed along with the order, but this is only optional! The basic BUY and SELL flags have no effect. Additional flags with effect are REDUCE_LONG and REDUCE_SHORT, which are meant for orders that reduce the position!
Running the Simulation
Once the strategy logic is define, the strategy is run from 1910 to 2005.
s.Evaluate( 1910, 2005 ); //16
The strategy's equity graph looks as follows:
_____________________________________________________________________
| Net
Equity Graph (EOD)
|
|
****+| H: 3978.1
|
****
|
|
*** | 3239
|
***** |
|
***
| 2816
|
***
|
|
***
| 2394
|
****
|
|
***
| 1972
|
*****
|
|
*********
| 1549
|
****
**
|
|
***
| 1127
|
**
|
|
***
| 704
|
**
|
|
* *******
| 282
|
****** **
|
|************************---------------------------------------------|
(0.0)
|03-Jan-1910
30-Dec-2005 |
|_____________________________________________________________________|
The strategy was profitable, making 3955 dollars over a 60 year timespan. The reason why no trading took place in the earlier part of the century (before ~1937) is because the SeasonalData() function in line //11 was intructed to accumulate 8000 data items before allowing the SeasonalPercentUp() function to perform any analysis.
A positive equity graph alone is of course not enough, especially since transaction costs were set to zero in lines //2 and //3, which is clearly unrealistic!
s.SetPerSideCommission(0.0); //2
s.SetPerSideSlippage(0.0); //3
Strategy performance needs to be analyzed in more detail before any real conclusions can be drawn.
_______________________________________________
|
|
|
Strategy Performance Report
|
|_______________________________________________|
Strategy Name:
'seasonal_demo'
Operational Bar:
24.Feb.1937-00:00:00:000
Last Bar:
30.Dec.2005-00:00:00:000
Bars Processed:
25954
Simulation Length (Years):
68.85
Bars per Year:
260
Last Trade was Open:
true {automatically
closed out at-market}
Risk Free Rate Of Return:
3.5
_____________________________________________________________________________
| SUMMARY
|
|
ALL |
LONG |
SHORT |
|_______________________________|________|___________|___________|___________|
| Net Profit
| [ccy] |
3,955.24 |
3,955.24 |
0.00 |
| Gross
Profit
| [ccy] |
3,955.24 | 3,955.24 |
0.00 |
| Total Costs
| [ccy] |
0.00 |
0.00 |
0.00 |
|
* Commission
| [ccy] |
0.00 |
0.00 |
0.00 |
|
* Slippage
| [ccy] |
0.00 |
0.00 |
0.00 |
| Winners Net
| [ccy] | 10,442.27 |
10,442.27 |
0.00 |
| Losers Net
| [ccy] | -6,487.03 |
-6,487.03 |
0.00 |
| Profit
Factor
| [rat] |
1.61 |
1.61 |
0.00 |
|_______________________________|________|___________|___________|___________|
| Largest
Drawdown
| [ccy] |
-285.35 |
|
|
|_______________________________|________|___________|___________|___________|
| Number Of
Trades
| [trad] |
1,221
| 1,221
|
0 |
| Percent
Winners
| [pct] |
59.05 |
59.05 |
0.00 |
|_______________________________|________|___________|___________|___________|
| Sum Bars -
All Trades
| [bars] | 4,883
| 4,883
|
0 |
| Perc. Bars
with Position
| [pct] |
18.81 |
18.81 |
0.00 |
|
|
|
|
|
|
|_______________________________|________|___________|___________|___________|
| RISK /
RETURN (ALL TRADES)
|
| Account A | Account B | Account C |
|_______________________________|________|___________|___________|___________|
| Start
Account Balance
| [ccy] |
285.35 |
570.70 | 856.06 |
| End
Account Balance
| [ccy] |
4,240.59 | 4,525.94 |
4,811.29 |
| Annualized
Rate Of Return
| [pct] |
4.00 |
3.05 |
2.54 |
| Annualized
Volatility (StDev) | [pct] |
5.58 |
3.68 |
2.88 |
| Sharpe
Ratio
| [rat] |
0.09 |
-0.12 | -0.33
|
|_______________________________|________|___________|___________|___________|
| Max Account
Balance
| [ccy] |
4,263.48 | 4,548.83 |
4,834.18 |
| Min Account
Balance
| [ccy] |
250.40 |
535.76 | 821.11 |
|
|
|
|
|
|
|_______________________________|________|___________|___________|___________|
| TRADES
|
|
ALL |
LONG |
SHORT |
|_______________________________|________|___________|___________|___________|
| Number Of
Trades
| [trad] | 1,221
| 1,221
|
0 |
| Ave Net
Profit
| [ccy] |
3.24
| 3.24 |
0.00 |
| Ave Gross
Profit
| [ccy] |
3.24 |
3.24 |
0.00 |
| Ave Costs
| [ccy] |
0.00 |
0.00 |
0.00 |
|
* Ave Slippage
| [ccy] |
0.00 |
0.00 |
0.00 |
|
* Ave Commission
| [ccy] |
0.00 |
0.00 |
0.00 |
| Ave Winner
/ Ave Loser
| [rat] |
1.12 |
1.12 |
0.00 |
| Ave Num
Transactions
| [tran] |
2.00 |
2.00 |
0.00 |
| Ave (Max)
Position
| [unit] |
1.96 |
1.96 |
0.00 |
| Ave Length
| [bars] |
4.00 |
4.00 |
0.00 |
| Ave
Positive Excursion
| [ccy] |
10.67 |
10.67 |
0.00 |
| Ave
Negative Excursion
| [ccy] |
-7.69 |
-7.69 |
0.00 |
|_______________________________|________|___________|___________|___________|
| Max
Positive Excursion
| [ccy] |
133.05 |
133.05 |
0.00 |
| Max
Negative Excursion
| [ccy] |
-124.97 | -124.97 |
0.00 |
| Max Num
Transactions
| [tran] |
2 |
2 |
0 |
| Max
Position Size
| [unit] |
10.49 |
10.49 |
0.00 |
| StDev Net
Profit
| [ccy] |
18.44 |
18.44 |
0.00 |
|
|
|
|
|
|
|_______________________________|________|___________|___________|___________|
| WINNERS vs
LOSERS
|
|
ALL |
LONG |
SHORT |
|_______________________________|________|___________|___________|___________|
| Number of
Winners
| [trad] |
721 |
721 |
0 |
| Number of
Losers
| [trad] |
500 |
500 |
0 |
| Average
Winner
| [ccy] |
14.48 |
14.48 |
0.00 |
| Average
Loser
| [ccy] |
-12.97 |
-12.97 |
0.00 |
| Largest
Winner
| [ccy] |
133.05 |
133.05 |
0.00 |
| Largest
Loser
| [ccy] |
-124.97 | -124.97 |
0.00 |
| Max
Consecutive Winners
| [trad] |
14 |
14 |
0 |
| Max
Consecutive Losers
| [trad] |
10 |
10 |
0 |
| Ave Winner
Length
| [bars] |
4.00 |
4.00 |
0.00 |
| Ave Loser
Length
| [bars] |
4.00 |
4.00 |
0.00 |
|
|
|
|
|
|
|_______________________________|________|___________|___________|___________|
| UNITS
|
|
ALL |
LONG |
SHORT |
|_______________________________|________|___________|___________|___________|
| Ave Net
Profit
| [ccy] |
0.8271 |
0.8271 | 0.0000 |
| Ave Gross
Profit
| [ccy] |
0.8271 |
0.8271 | 0.0000 |
| Ave Costs
| [ccy] |
0.0000 |
0.0000 | 0.0000 |
| Ave
Slippage
| [ccy] |
0.0000 |
0.0000 | 0.0000 |
|_______________________________|________|___________|___________|___________|
| Num Units
Traded
| [unit] | 4,782.31 |
4,782.31 |
0.00 |
|
* At-Market
| [unit] | 4,782.31 |
4,782.31 |
0.00 |
|
* On-Stop
| [unit] |
0.00 |
0.00 |
0.00 |
|
* At-Limit |
[unit] |
0.00 |
0.00 |
0.00 |
| Perc. Units
w/o Slippage
| [pct] |
0.00 |
0.00 |
0.00 |
|
|
|
|
|
|
|_______________________________|________|___________|___________|___________|
|
TRANSACTIONS
|
|
ALL |
|
|
|_______________________________|________|___________|___________|___________|
| Number of
Transactions
| [tran] | 2,442
|
|
|
|
* At-Market
| [tran] | 2,442
|
|
|
|
* On-Stop
| [tran] |
0 |
|
|
|
* At-Limit
| [tran] |
0 |
|
|
|
|
|
|
|
|
|_______________________________|________|___________|___________|___________|
| DRAWDOWNS
|
|
ALL |
|
|
|_______________________________|________|___________|___________|___________|
| Largest
Drawdown
| [ccy] |
-285.35 |
|
|
| Longest
Drawdown
| [bars] | 1,512
|
|
|
| Number of
Drawdowns
| [ddn] |
304 |
|
|
| Ave Length
| [bars] |
50.48 |
|
|
| Ave
Drawdown
| [ccy] |
-20.69 |
|
|
| Current
Drawdown
| [ccy] |
-130.48 |
|
|
|_______________________________|________|___________|___________|___________|
Note how the Performance Report only shows
statistics for 'long' trades. This is of course because the strategy did not
engage in any short selling.
Trade Report
________________________________
|
|
|
Trade Report
|
|________________________________|
Strategy Name: 'seasonal_demo'
Table Name:
'SEASONAL_DEMO_TRADE_LOG'
Number of Trades: 1221
__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
| NUM |
DIRECTION| MAX_POSITION| ENTRY_PRICE| EXIT_PRICE| PL_BEFORE_SC| PL_AFTER_SC|
CUMM_PL_AFTER_SC| NUM_BARS| ENTRY_BAR_DATETIME| ENTRY_INTERVAL|
EXIT_BAR_DATETIME| EXIT_INTERVAL| COMMISSION| SLIPPAGE| NUM_TRANSACTIONS|
NUM_TRANSACT_WO_SLIPPAGE| EXIT_ORDER_REF_NUM| POS_EXCURSION| NEG_EXCURSION|
IS_OPEN| TOTAL_NUM_UNITS| NUM_UNITS_WO_SLIPPAGE|
|_____|__________|_____________|____________|___________|_____________|____________|_________________|_________|___________________|_______________|__________________|______________|___________|_________|_________________|_________________________|___________________|______________|______________|________|________________|______________________|
|
1|
LONG|
5.754|
173.790|
171.590|
-12.659|
-12.659|
-12.659|
4|
25-May-1937|
NIL|
01-Jun-1937|
NIL|
0.000| 0.000|
2|
0|
5|
5.294|
-12.659| false|
11.508|
0.000|
|
2|
LONG|
5.906|
169.320| 177.740|
49.728|
49.728|
37.069|
4|
30-Jun-1937|
NIL|
07-Jul-1937|
NIL|
0.000| 0.000|
2|
0|
8|
49.728|
0.000| false|
11.812|
0.000|
|
3|
LONG|
5.484|
182.350| 184.420|
11.352|
11.352|
48.421|
4|
21-Jul-1937|
NIL|
26-Jul-1937|
NIL|
0.000| 0.000|
2|
0|
11|
13.710|
0.000| false|
10.968| 0.000|
| 4| LONG| 5.356| 186.720| 189.340| 14.032| 14.032| 62.453| 4| 11-Aug-1937| NIL| 16-Aug-1937| NIL| 0.000| 0.000| 2| 0| 16| 17.674| 0.000| false| 10.711| 0.000|
( . . . )
|_____|__________|_____________|____________|___________|_____________|____________|_________________|_________|___________________|_______________|__________________|______________|___________|_________|_________________|_________________________|___________________|______________|______________|________|________________|______________________|
| NUM |
DIRECTION| MAX_POSITION| ENTRY_PRICE| EXIT_PRICE| PL_BEFORE_SC| PL_AFTER_SC|
CUMM_PL_AFTER_SC| NUM_BARS| ENTRY_BAR_DATETIME| ENTRY_INTERVAL|
EXIT_BAR_DATETIME| EXIT_INTERVAL| COMMISSION| SLIPPAGE| NUM_TRANSACTIONS|
NUM_TRANSACT_WO_SLIPPAGE| EXIT_ORDER_REF_NUM| POS_EXCURSION| NEG_EXCURSION|
IS_OPEN| TOTAL_NUM_UNITS| NUM_UNITS_WO_SLIPPAGE|
|_____|__________|_____________|____________|___________|_____________|____________|_________________|_________|___________________|_______________|__________________|______________|___________|_________|_________________|_________________________|___________________|______________|______________|________|________________|______________________|
| 1216|
LONG|
0.094| 10,594.410|
10,513.450|
-7.642|
-7.642|
3,925.803|
4|
10-Aug-2005|
NIL|
16-Aug-2005|
NIL|
0.000| 0.000|
2|
0|
4098|
8.635|
-7.642| false|
0.189|
0.000|
| 1217|
LONG|
0.095| 10,519.580|
10,463.050|
-5.374|
-5.374|
3,920.430|
4|
23-Aug-2005|
NIL|
29-Aug-2005|
NIL|
0.000| 0.000|
2|
0|
4100|
0.000|
-11.625| false|
0.190|
0.000|
| 1218|
LONG|
0.096| 10,412.820|
10,589.240|
16.943|
16.943|
3,937.372|
4|
30-Aug-2005|
NIL|
06-Sep-2005|
NIL|
0.000| 0.000|
2|
0|
4103|
16.943|
0.000| false|
0.192|
0.000|
| 1219|
LONG|
0.096| 10,402.770|
10,522.590|
11.518|
11.518|
3,948.890|
4|
28-Oct-2005|
NIL|
03-Nov-2005|
NIL|
0.000| 0.000|
2|
0|
4105|
11.518|
0.000| false|
0.192|
0.000|
| 1220|
LONG|
0.093| 10,755.120|
10,883.510|
11.938|
11.938|
3,960.828|
4| 08-Dec-2005|
NIL|
14-Dec-2005|
NIL|
0.000| 0.000|
2|
0|
4107|
11.938|
0.000| false|
0.186|
0.000|
|_____|__________|_____________|____________|___________|_____________|____________|_________________|_________|___________________|_______________|__________________|______________|___________|_________|_________________|_________________________|___________________|______________|______________|________|________________|______________________|
| 1221|
LONG|
0.093|
10,777.770| 10,717.500|
-5.592|
-5.592|
3,955.236|
3|
27-Dec-2005|
NIL|
30-Dec-2005|
NIL| 0.000|
0.000|
2|
0|
0|
1.716|
-5.592| true|
0.186|
0.000|
|_____|__________|_____________|____________|___________|_____________|____________|_________________|_________|___________________|_______________|__________________|______________|___________|_________|_________________|_________________________|___________________|______________|______________|________|________________|______________________|
Recall that this strategy opted to trade the Dow Jones Index as it would a stock. The Trade Report above shows that the first trade in 1937 bought 5.7 'shares' of the Dow, whereas that last trade only bought 0.093 'shares'. This is because the strategy opted to trade a fixed 'cash amount' rather than a fixed 'number of shares', to avoid some of the distortion introduced by inflation. Another method to deal with inflationary forces would have been to use an 'inflation adjusted' time series of the Dow.
Checking Performance against Random Signals
One of the benefits of using Time Series API is that it is a stand-alone backtesting engine. There are no restrictions of any kind on either the models or the simulations. For example, running a strategy iteratively, for optimization purposes or in the context of a Monte Carlo simulation is not a problem. In some situations, when the strategy uses particularly complex multi-leg trades, it may even make sense to run a separate simulation one trade at a time, to cleanly separate trades that normally would overlap!
In light of the results from the earlier example of the seasonaly based strategy, it would be interesting to verify whether the seasonal effect it found truly exists or whether it's apparent profitability is merely imaginary, caused by strong inflationary forces.
One way to find out is to repeatedly run a strategy that generates approximately the same number of trades over the same time period, but which relies on random buy signals instead!
Such a simulation can be set up quickly and easily!
double netPLSum = 0.0;
int numTradesSum = 0;
for(int c=0; c< 10; c++)
{
Strategy s;
s.SetOrderSizeAs(CASH_AMOUNT);
s.SetBigPointValue(1.0);
s.SetPyramidingEnabled(false);
Tuple data = IRecord(db, "indu.index.close.daily",
PRICE_MASTER | TIME_MASTER );
MarketOrder( LONG, 1000.0, RandBool(0.09), THIS_CLOSE ); //6 - Random signal
MarketOrder( EXIT_LONG, BarsInTrade() == 4, THIS_CLOSE );
s.Evaluate( 1937, 2005 );
netPLSum += s.GetNetProfit(); //Aggregating profit
numTradesSum += s.GetTradeCount(); //Aggrevating trade count
}
file << "Combined num trades " << numTradesSum << endl;
file << "Combined net profit " << netPLSum << endl;
file << "Combined profit per trade" << netPLSum / (double)numTradesSum << endl;
The original buy signal is replaced by a random signal produced by the RandBool() function. The probablity of a signal is set to 9 percent which produces about 1200 trades between 1937 and 2005, similiar to the earlier strategy.
This time, the start date is set to 1937, since it took the earlier strategy 8000 trading days to accumulate enough data to start trading.
The Net Profit and Trade Count produced by each simulation are accessed via the strategy's GetNetProfit() and GetTradeCount() members and are aggregated. The output produced by the strategy is:
Combined num trades: 11821
Combined net profit: 15470.8
Combined profit per trade: 1.30875
These strategies produced 1.3 dollars in profit per trade, compared to 3.24 dollars for the earlier strategy! So, more that half of the profits generated by the earlier strategy is due to genuine seasonal effect.
Other Applications
This same iterative approach could be used to run Monte Carlo simulations, potentially to value options with complex features such as 'barrier', 'strike out' and 'range' type features which would be encoded in the strategy logic as exit orders if certain conditions are met, such as a 'strike' being hit.
Since valuing an option often involves looking into the 'future', a way to generate 'future' data is required. The RandomWalk() function can be used to generate random time series with normally distributed price changes. Since real price changes in finance are not always 'normally distributed', it is possible to customize the RandomWalk() function with proprietary algorithms.
Copyright (c) 2008 PERITECH. All Rights Reserved.