Introduction to Auto Trading

My colleague Dave Di Marcantonio runs several web sites, including AmiBroker Courses where I sell some of my training courses. Dave recently published an introduction to trading automation, sometimes referred to as auto trading. Dave’s course also provides details about Alera Portfolio Manager, a new trading automation solution. APM can be integrated directly with AmiBroker but also works with other signal generating tools.

If you’re interested in automating your quantitative trading strategies, I encourage you to check out the course. Use the coupon code quantforhire to receive a $50 discount at checkout.

Trading or Investing Automation for AmiBroker Users made Simple

Beat the Market with a Simple SuperTrend Strategy

Recently I gave a presentation describing the process of creating and validating a simple trading strategy using AmiBroker. In this case, the instruments traded were the NIFTY and Bank NIFTY indices from the NSE in India, and the primary indicator used was the SuperTrend indicator. Although the performance results of the strategy are quite respectable, the real purpose was to introduce traders to the tools and methodologies that can be used to develop effective strategies of their own.

You can view the presentation linked above (my apologies for the less-than-stellar quality), or you can simply review the slide deck. As always, let me know if you have questions or comments. AFL used for the tests and an Excel workbook containing results are available here.

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 bands above and below the current average price. The SuperTrend line follows the lower band when the price is in an up trend (has most recently broken the upper band), and follows 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!