Combining Strategies with AmiBroker

I’m a big fan of AmiBroker, particularly the speed and flexibility it provides for backtesting trading strategies. However, one thing that is not particularly straightforward in AmiBroker is combining multiple strategies into a single system. Many quantitative (a.k.a. systematic) traders, myself included, use more than one strategy to help smooth their portfolio returns. The idea is that when one strategy is underperforming or is in a drawdown, other strategies can “pick up the slack” and minimize the pain. This works best when the individual strategy returns are not highly correlated.

If you’re trading your portfolio from a single brokerage account, then the most accurate way to model trading multiple strategies is to build all the logic into a single backtest. Before we can even start down that path, we would need to decide how the strategies would coexist in live trading. Some questions to consider include:

  1. Will you allocate a portion of your portfolio equity to each strategy, or will all strategies use a common pool of equity?
  2. If allocating fixed percentages, will there be a periodic rebalancing? For example, if Strategy A is allocated 75% of equity and Strategy B is allocated 25%, then at the end of the month/quarter/year will you allocate 75% of the current equity for Strategy A or will it continue to use the compounded balance that it started with?
  3. If operating from a common pool, will you try to ensure that one strategy does not dominate?
  4. Will the strategies operate independently, or will trades opened by one affect the behavior of the other? For example, assume that Strategy A opens a long position in AAPL on Monday. If Strategy B signals a long entry on Wednesday, do you buy additional shares or skip the trade? What if B signals a short entry?
  5. What if both strategies signal an entry on the same day: does one have priority over the other?
  6. Will the exit for a position be dependent on the strategy that triggered the entry? If allowing multiple concurrent entries from different strategies, how will this complicate the exits?

All of the issues above can handled via AmiBroker’s Custom Backtest (CBT) interface along with liberal use of static variables. However, it won’t be a trivial exercise.

Another approach, although imperfect, is to simply combine the equity curves of the two strategies. At a high level, this is quite straightforward:

  1. Save the equity curve for each strategy as an artificial ticker symbol in your AmiBroker database.
  2. Run a new back test in which you “buy” each equity curve as if it were a tradeable instrument. The “position size” for each trade corresponds to the portion of your equity that you allocate to each strategy.
  3. To rebalance your portfolio based on the target allocations, simply sell all positions and then buy them again at the appropriate position sizes, or you can get fancy and scale in and out to adjust the position sizes.

The advantages of combining equity curves in this way are:

  1. The implementation is quite easy and can probably be done in a very reusable way. For example, in each of the base strategies that you’ll be combining you could add something as simple as this to the end of a high-level CBT:
         flags = atcFlagDefaults + atcFlagEnableInPortfolio;
         AddToComposite(bo.EquityArray, “~eqStrategyA”, "C", flags);
  2. AmiBroker will be able to accurately determine CAR, Max Drawdown, and other portfolio metrics for the combined system. With a little work, you can also report the correlation of the returns of the base strategies.

Of course, any shortcut will come with some disadvantages as well, including:

  1. Each strategy must be assigned an equity allocation. I have not yet come up with a good way to implement the “shared pool” model described above when combining equity curves.
  2. There will be no trade-specific metrics like number of trades, win rate, average % P/L, etc. because we lost that granularity when we saved only the equity curve.
  3. No special handling of overlapping trades for the same symbol.
  4. Any “rebalancing” that you do assumes that you can simply shift a portion of your equity from one strategy to another without penalty, even though in live trading you would incur commissions as you closed and reopened the individual positions to reflect the new equity allocations.

I recently built my own Equity Combo utility to combine two equity curves. Why only two at a time? Because rather than just combining one equity curve for Strategy A with one for Strategy B, I wanted to start by running optimizations on both strategies, saving an equity curve for each variation produced by the optimization run. My Equity Combo utility then allows me to examine all possible combinations of a Strategy A equity curve with a Strategy B equity curve, reporting the following metrics for each combination:

  • CAR
  • Max Drawdown
  • CAR/MDD
  • Annual Volatility of Returns
  • Correlation with SPX
  • Correlation between A and B
  • Sharpe Ratio
  • % of Months with Positive Return
  • % of Months with Negative Return

Notice that all my metrics can be determined from the individual and/or combined equity curves, without any knowledge of individual trades. You would surely have your own list of metrics that are of interest.

My utility also allows me to specify the percentage of equity allocated to A and B, as well as the rebalance frequency (Never, Daily, Weekly, Monthly, Quarterly, Annually). And finally, it can save each of the combined equity curves so that I can combine one or more of them with yet another base strategy (Strategy C) to find the total effect of adding new strategies to my portfolio.

The best part? The entire utility is only about 450 lines of AFL code, even with ample comments, blank lines for readability, etc. If you’re interested in learning more, please feel free to contact me.

SuperTrend Indicator

I recently completed a client project that utilizes the SuperTrend indicator. The indicator is basically a variation on other types of volatility bands, using a multiple of ATR to define the define the band above and below the current average price. The SuperTrend line is the lower band when the price is in an up trend (has most recently broken the upper band), and is the upper band when the price is in a down trend (has most recently broken the lower band).

I have not used the indicator enough to have a strong opinion on its usefulness, although it seems to be particularly popular with traders from India. However, I had a hard time finding written definitions of the indicator, and the one that I did find was incorrect. I finally resorted to creating a definition by reverse engineering some code written by Rajandran R at www.marketcalls.in. I then wrote my own version in AmiBroker AFL in a way that makes it easy to use on a chart or in an exploration. In addition, the SuperTrend function could be easily copied to another AFL used for back testing or other AmiBroker analysis.

Here is the definition of the SuperTrend indicator that I derived from Rajandran’s code:

SuperTrend requires two parameters:

  1. lenATR: The number of periods used to calculate ATR
  2. width: The factor by which to multiply the ATR value when determining upper and lower bands

We begin by calculating preliminary values for the upper and lower bands as follows:

UpperBand = (High + Low) / 2 + (width * ATR(lenATR))
LowerBand = (High + Low) / 2 - (width * ATR(lenATR))

Next we examine the bands and the Close of each bar in the series to determine the trend:

If Current Close > Previous UpperBand Then Trend is UP
Else If Current Close < Previous LowerBand Then Trend is DOWN
Else Trend is unchanged from previous bar

The direction of the trend allows us to modify the preliminary band values, and also determine whether the SuperTrend line is currently following the upper or lower band:

If UP trend Then 
    Current LowerBand = max(Current LowerBand , Previous LowerBand )
    Current Supertrend = Current LowerBand
If DOWN trend Then 
    Current UpperBand = min(Current UpperBand , Previous UpperBand )
    Current Supertrend = Current UpperBand

Here is the AmiBroker AFL for the SuperTrend indicator:

/////////////////////////////////////////////////////////////////////////////
// Supertrend Indicator
//
// AmiBroker implementation by Matt Radtke, www.quantforhire.com
//
// History
// v1: Initial Implementation
/////////////////////////////////////////////////////////////////////////////

function SuperTrend(lenATR, width)
{
    nATR = ATR(lenATR);
    pAvg = (H+L) / 2;

    upperBand = pAvg + width * nATR;
    lowerBand = pAvg - width * nATR;
    isUpTrend = True;
    dn = DateNum();

    for (i=lenATR; i<BarCount; ++i)
    {
        if (C[i] > upperBand[i-1])
            isUpTrend[i] = True;
        else if (C[i] < lowerBand[i-1])
            isUpTrend[i] = False;
        else
            isUpTrend[i] = isUpTrend[i-1];

        if (isUpTrend[i])
            lowerBand[i] = Max(lowerBand[i], lowerBand[i-1]);
        else
            upperBand[i] = Min(upperBand[i], upperBand[i-1]);
    }

    super = IIf(isUpTrend, lowerBand, upperBand); 
    return super;
}

lengthATR = Param("ATR Length", 10, 1, 100, 1);
widthBands = Param("Band Width", 3, 1, 20, 0.1);
st = Supertrend(lengthATR,widthBands);

Plot(st, "Supertrend("+lengthATR+","+widthBands+")", ParamColor( "Color", colorCycle ), ParamStyle("Style") );

Filter = True;
AddColumn(O,"Open");
AddColumn(H,"High");
AddColumn(L,"Low");
AddColumn(C,"Close");
AddColumn((H+L)/2,"Avg");
AddColumn(ATR(lengthATR), "ATR("+lengthATR+")");
AddColumn(st, "Supertrend("+lengthATR+","+widthBands+")");

Recent Projects

In addition to my thriving consulting business, other activities have been keeping me quite busy lately. I’ve been working with my friend and colleague, Cesar Alvarez, to roll out a new trading strategy named Snap Dragon. You can read more about it here: https://www.tranquilitytrading.com/snap-dragon-main/

Cesar is offering a 10% discount to anyone who signs up before the end of July 2018.

I also helped with the research for Larry Connors’ new book, Buy The Fear, Sell The Greed – 7 Behavioral Quant Strategies For Traders.  Larry is providing a free preview of the first chapter if you’re interested in taking a look.

 

Cesar’s First Rule

Much of what I know about the practical aspects of backtesting I learned from two of my colleagues at Connors Research, Cesar Alvarez and David Weilmunster. One particularly important lesson was Cesar’s observation that “if the results seem too good to be true, they probably are”. This situation comes up often enough that I now think of it as “Cesar’s First Rule”.

Not long ago, a client asked me to verify some very strong backtesting results produced by another researcher. I began by looking through the other developer’s AmiBroker AFL code, but didn’t immediately find any errors. However, as any of you who’ve done a significant amount of coding are aware, it can be challenging to dive into someone else’s work and fully grasp all the nuances.

Next I started to rewrite the strategy using my own coding templates, and sure enough, my results were not nearly as good as those from the other developer. It turned out that he was always exiting at the profit target price, even when the price had declined and the trade should have resulted in a stop loss! Since AmiBroker’s default behavior is to restrict the price based on the high and low of the exit bar, some trades still came through as losers  but with a smaller loss than they should have. This had the effect of masking the problem, because almost anyone would have recognized that 100% winners must be an error.

Sometimes even my own results are too good to be true. Recently I was working on a presentation about building adaptive strategies: quantified trading systems that use different rule parameters depending on the current market condition. I was quite pleased when the adaptive version of the strategy outperformed every variation of the static-parameter version over the same time period (2013-2016). But then, Cesar’s first rule started nagging at me. I checked all my code, and all the adaptive parameters, but everything looked OK. Finally I realized that I had selected the adaptive parameters using results from the current time period (2013-2016), which is basically “cheating” by looking into the future. When I selected the parameters based on past results (2000-2012) as I should have done, my adaptive strategy did not perform nearly as well.

Everyone wants to find their own silver bullet of trading. We all hope that we have the ability to make a brilliant discovery. The cold, hard truth is that in most cases if the results seem too good to be true, then there’s a mistake lurking somewhere. Thanks for the lesson, Cesar!

Strategy Tuning with Market Types

In a previous post we examined how back test results could be summarized for different market types. In today’s post, we’ll look at how we can use that information to tune our strategy for live trading.

As the basis for this exercise, we will use a modified version of the short mean reversion strategy known to Trading Markets customers as Bear Over-Reaction. While I cannot divulge all the specifics of this proprietary system, the basic rules are as follows:

A Setup occurs when:

  1. Stock is a current member of the Russell 1000
  2. Closing price is greater than $10
  3. 21-day average volume is greater than 2.5M
  4. The stock has closed higher for [3,4,5] days in a row
  5. Today’s close is greater that [50,75] % of the range between today’s low and high

Short the stock on the day following Setup using  a limit order  [2,4,6,8] % above the Setup close. Up to 5 open positions are allowed, with 20% of available equity allocated to each position.

When any of the following conditions occur on the close, Cover the position at the next open:

  1. Two-period RSI is less than 30
  2. An EOD Stop Loss [10,25] % above the entry price
  3. An EOD Profit Target [5,10,15] % below the entry price
  4. A Time Stop when the trade has been open for 15 trading days

The values shown inside square brackets are the parameters that we will test during our AmiBroker optimization. Note that 3 x 2 x 4 x 2 x 3 gives us 144 unique parameter combinations or strategy variations.

To begin, we will test each of the 144 variations from 1/1/2000 through 12/31/2010. Summary results for the 10 variations with the highest Compound Annual Return (CAR) are shown in the table below.

Results-All-2000
Table 1: 2000-2010

While the return numbers are not bad, the Max Drawdown (MDD) values are pretty scary. Not too many traders have the stomach to lose 60-70% of their trading capital. Even so, this gives us a place to start.

Now let’s jump ahead and see how these top performers did over the next 5+years, from January 1, 2001 through January 31, 2016:

Results-All-2011
Table 2: 2011 – Jan. 2016, Results for Top 10 from 2000-2010

Uh oh! Here we see proof of the ever-popular warning “past performance is not a guarantee of future results”. Of the Top 10 variations for our initial 11-year test period, only one of those variations was still in the top ten over the next 5 years (variation 179). The new Top 10, shown below, uses different combinations of the parameter values:

Results-All-20110-Top10
Table 3: 2011- Jan 2016, Top 10

The worst part is that we had no way to predict which variations would be the most successful following our initial testing. The best CAR was produced by the variation that ranked 19th over the previous test period! This is where looking at different market conditions or types can help.

Going back to our results from 2000-2011, we will now add columns for each of six market types: Bear, Neutral, or Bull for market price action, and Quiet or Volatile for market volatility. For each market type, we have summarized the total dollars of Profit/Loss for trades whose Setup occurred when that market type was in effect.

Results-MktType-2000
Table 4: 2000-2010, Top 10 with Market Types

Notice that variation 275, which produced the highest overall CAR, also produced a decent amount of profit in Bear markets as well as Quiet Bull markets. However, it was more or less break even in Volatile Neutral markets, and lost lots of money in Quiet Neutral and Volatile Bull markets.

Instead of selecting one set of input parameters (i.e. one variation) to trade in all markets, what if we tried to use the best parameters for each market type. You could define “best” any number of ways, but for now we’ll stick with the very simple metric of total dollars of profit. If we re-sort our results to show the variations with the highest total P/L for V. Bear markets, we get:

Results-2000-VBear
Table 5: 2000-2010, Highest V. Bear P/L

In our strategy optimized for market type, on any day classified as Volatile Bear we would use an Up Close Streak of 3 days and a Closing Range of 75% for the Setup. We would then enter the trade on a 2% limit order, and use a stop loss of 25% and a profit target of 10%.

Repeating the process for the Quiet Bear market type, we see:

Results-2000-QBear
Table 6: 2000-2010, Highest Q. Bear P/L

Therefore, on Quiet Bear days our Setup would also require an Up Close Streak of 3 days and a Closing Range of 75%, just like the Volatile Bear Setup. However, the limit entry would be 4% above the Setup close, and the stop loss would be at 25% and the profit target at 15%.

Repeating yet again for the Quiet Neutral market type, our results show:

Results-2000-QNeutral
Table 7: 2000-2010, Highest Q. Neutral P/L

Now we can see that there are actually variations that have done well in this market type. On Quiet Neutral days, our Setup would be defined by a 4-day Up Close streak and a 75% Closing range. We would enter on a 6% limit order, with stops and targets of 25% and 5% respectively.

We continue this process for the remaining market types, and then combine the strategy parameters for each market type into one optimized strategy. Keep in mind that all of our strategy parameters were selected based on the back test results from 2000-2010.

So how does our optimized strategy perform from 2011 through January 2016?

Results-Optimized
Table 8: 2011-Jan. 2016, Optimized Strategy

The Neutral market types still lost money. However, compare these results to Table 2, which showed how the former Top 10 from 2000-2010 performed from 2011 through January 2016. The optimized results shown in Table 8 had a higher CAR than any shown in Table 2, and had lower MDD than all but one of those variations. Best of all, we actually had a systematic way of selecting our strategy parameters.

In this example, we used a very simple method for defining the “best” results for each market type: the largest profit. We did not take into account drawdowns or any other factors that might be important to you when you trade. With additional thought and testing, it’s likely that we could improve on how to select the best variations for each market type. In addition, we would probably repeat the baselining process on a periodic basis rather than doing it just once and then using those “optimized” parameters for the next five years. Hopefully this article has given you some ideas on how to tune your own strategies for market conditions. As always, you can  contact me if you need assistance.