Analyzing the Performance of Acorns Investment Portfolios using Quantopian

I’ve been using Acorns, the app to “help anyone invest” for a little over a year, mainly as a curiosity. One common theme I’ve seen amongst Acorns users is the disappointment with the lack of returns throughout their usage. Simply searching “Acorns Returns” online brings up hundreds of posts (many of them, uncoincidentally, occurring on r/investing*). While many chalk down to unrealistic users irritated they haven’t seen their investment quadrupling in value overnight, reading these posts did make me question: what historical data in regards to returns is available for the different Acorns portfolios?

Digging around, I quickly realized the answer was: not much. Acorns itself publishes no info on the rate of returns of its various portfolios, which isn’t unexpected or surprising. Reading the accounts of other users doesn’t cut it for me, since many of them bail after getting antsy or taking their first 2% loss. There’s another camp of users who claim they’ve seen some absurd rate of return (Bro, I’ve seen 65% since May!). What about portfolio statistics? Dreaming of reading about portfolio volatility? Keep dreaming. Point being, tangible data is hard to come by.

Coincidentally, I’ve been playing around with Quantopian, an absolutely awesome python-based algorithmic investing framework and simulation/backtesting platform. Quantopian has access to stock metrics, such as price, volume, dividend/split info, etc, since January 3rd, 2002; Quantopian allows users to backtest stocks and trading algorithms using this data, in order to examine performance of various trading strategies. Somewhere along the line, I had the thought: “Why not just create each of the Acorns portfolios and backtest them to analyze their performance?”. The data provided by these backtests is generally so close to true performance for simple cases that any error is excusable.

Acorns allows users to choose a portfolio based on their risk tolerance and investing goals, all ranging from “Conservative” to “Aggressive”. The five portfolios are really just different target allocations in six ETFs: VOO, VB, VWO, VNQ, LQD, and SHY. The first four funds are Vanguard ETFs and the last two are iShares bond ETFs. The portfolio distributions are shown below, with the Aggressive portfolio being shown on top and the Conservative portfolio on the bottom.

acorns

This is basically all the info required to build these portfolios within Quantopian. Each security in the portfolios is added to the trading list, and  a rebalance function is scheduled to run every day 1 hour after the market opens. In reality, the Acorns portfolio rely on new round-ups and contributions to keep allocations at the right percentages, but this method of simulation seems close enough. I also set the backtest to only hold long positions, and made sure the trading commissions were zero (so as to only focus on the portfolio performance, and not the Acorns fee breakdown). The entire algorithm is shown below.

"""
Attempts to model the Acorns Agressive Portfolio for performance modeling.
Backtesting is limited to September 10, 2010 (first trade day of $VOO).
Acorns keeps allocations exact by purchasing fractional shares. Since this isn't generally possible, a larger initial capital must be used (>$10K).
"""

def initialize(context):
 """
 Called once at the start of the algorithm.
 """ 
 set_long_only()
 
 # Each security in the ETF along with the target percentage
 context.voo = (sid(40107), 0.14) 
 context.vb  = (sid(25899), 0.25) 
 context.vwo = (sid(27102), 0.20) 
 context.vnq = (sid(26669), 0.30) 
 context.shy = (sid(23911), 0.05) 
 context.lqd = (sid(23881), 0.06) 
 
 context.security_list = [context.voo, context.vb, context.vwo, context.vnq, context.shy, context.lqd]
 
 # Rebalance every day, 1 hour after market open.
 # In reality, the Acorns app banks on additional buys 
 # To keep the allocations correct.
 schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
 
 # Fees are zero, if you're on the "student plan". 
 set_commission(commission.PerShare(cost=0, min_trade_cost=0))
 set_commission(commission.PerTrade(cost=0))
 
 
def rebalance(context,data):
 """
 Execute orders according to our schedule_function() timing. 
 """
 for security in context.security_list:
   #rebalance to target percentage
   if data.can_trade(security[0]):
     log.info("Rebalancing %s to %s percent" % (str(security[0]), str(security[1])))
     order_target_percent(security[0], security[1])

The only thing that changes between each of the Acorns portfolios is the target percentages. Everything else remains constant, which makes backtesting and examining the different portfolios incredibly easy. Three backtest start dates were chosen: September 10th, 2010, September 10th 2014, and October 8th 2015. Each backtest ended on the same date: October 7th 2016. The returns (%) for each backtesting window are summarized below.

screen-shot-2016-10-09-at-1-04-39-am

Upon first look, it appears that the returns are reasonable and on par for each portfolio description, and I can confirm that the returns are very close to the actual returns I’ve experienced during my usage of the app over the last year. Returns diminish fairly monotonically from aggressive to moderate as one would expect.

There’s a catch though: I didn’t include the S&P500 benchmark performance in the results.

screen-shot-2016-10-09-at-1-12-23-am

In all the backtest windows, the S&P500 destroys the Acorns portfolios. Suddenly, the 10.57% short term return doesn’t seem so great after realizing the S&P500 did almost 13 percent in the same year. Take a look at the Quantopian backtest results for the aggressive portfolio, the best-performing one of the bunch, over the last year.

screen-shot-2016-10-09-at-1-16-59-am

The portfolio almost exclusively underperforms the S&P500 in terms of returns. It’s also worth nothing that the drawdown during the January-Feburary time period of 2016 is worse than that of the S&P500 as well.

At this point, the logical thought is “Of course the drawdown sucks, because this is the Aggressive portfolio.” And you’d be partially right. Take a look at the results for the conservative portfolio during the same time period:

screen-shot-2016-10-09-at-1-21-52-am

The drawdown during the same period is hardly better than the S&P500, which fell over 3% from the starting point in the beginning of October 2015. Rather disappointing, given that the portfolio is billed as a “safer bet” option for investors. The backtest results over the last year of all five portfolios are given below, for anyone interested.

screen-shot-2016-10-09-at-1-16-59-am screen-shot-2016-10-09-at-1-40-41-am

screen-shot-2016-10-09-at-1-41-00-am

screen-shot-2016-10-09-at-1-41-20-am

screen-shot-2016-10-09-at-1-40-16-am

My final thoughts? It’s not that these portfolios are bad. They’re decent portfolios, especially for beginning investors who are looking to stash away a few pennies here and there with minimal effort and a simple fee structure. Is a 6-10% return better than holding onto a pile of cash and losing out to inflation? Absolutely! An Acorns portfolio serves as an excellent “baby’s first investment”. I just can’t help but shake the disappointment that all five portfolios have underperformed the market in most, if not all, previous financial circumstances. For anyone beyond the novice saver/pocket-change investor, it makes more sense to invest wisely in a few basic ETFs (i.e. NOBL/UPRO) and call it a day. A true zero-fee investment experience can be had by using Robinhood, potentially saving the $1 per month fee. Smart savers who appreciate the auto round-up feature of Acorns can enjoy Wealthfront‘s auto-deposit feature with the added benefit of Tax-Loss harvesting.

Do you have thoughts on my analysis of these portfolios or my backtesting strategy? Let me know in the comments. I appreciate reader input.

*r/investing deserves a post of its own. Similar to r/fitness, the “beginners pretending to be experts” culture leads to an unsurprising amount of awful advice. Trust the internet at your own risk. Trust the internet with investment guidance at your bank account’s risk.

Adding a buzzer beeper to the Illuminati32/Tarot Naze 32 Flight Controller

My buddy just bought an Illuminati32 FC board from Hobbyking for his new ZMR180 miniquad. The board is pretty sweet: Naze32, MWOSD, 35x35mm form factor, and only 20 bucks (on sale). It was really easy to set up, especially with the ZMR180 PDB that Diatone is shipping. The biggest problem is that there’s no buzzer output! A buzzer driver is one basic necessity every flight controller should come with. Low battery, lost models, and mode change beeps are pretty crucial to the operation of these miniquads. Luckily, it’s relatively simple and very cheap to add one onto this board. You’ll need:

  • High gauge (>30) wire
  • Fine solder and a fine-tipped soldering iron
  • An NPN BJT (2N3904, BC557, etc)
  • A 100Ω resistor
  • Heatshrink tubing or tape

The built-in Naze32 (rev5 or greater) buzzer driver is a NPN transistor used in an open-collector configuration. There is a base resistor on the order of 100Ω used to set the drive current. PA12, or Pin 33 of the STM32 is used to drive the BJT.

buzz

It’s possible to do this BEAM style, with SMT components and a small piece of protoboard, or even a with small PCB (perhaps from OSHPark). We chose to do it with a little PCB that’s also got an ATTiny84 on it which controls some RGB led strip.

buzz2

The hardest part of this is soldering onto the STM32, since PA12 isn’t broken out or used for anything else. See the below image to find the pin you need to solder to. Note that the text on the STM32 isn’t guaranteed to be upright, so look for the pin 1 marker!!!

stm

You’ll need some fine gauge wire (30 gauge wire wrap wire worked well), a fine-tipped soldering iron, and a steady hand.

IMG_0593

Once the solder connection is made, don’t hesitate to dump some hot glue onto the connection to keep it from being broken loose. Once you’ve got the connection to PA12, solder one end of the 100Ω resistor to the PA12 wire, and the other to the base of the BJT. Solder the positive lead of your buzzer to the flight controller’s 5V input, and the negative lead of the buzzer to the collector of the BJT. Finally, solder the emitter of the BJT to ground. Be sure to wrap everything in heatshrink or tape so that you don’t accidentally short anything out.

That’s all there is to it! Just plug in a battery without turning on the transmitter and the FC should issue the “no connection” beep if everything worked.

Tricopter Build!

I’m going to be posting chunks of the Tricopter 2.0 build as I go along with it. So far, I’ve ordered the new power system (4S Lipo), and am working on the vibration damping system. I think we’ve got something good here. I’ll post pics as they come along assuming they’re interesting enough. I’ve got a shoddy shot of the freshly laser-cut frame and camera mount and I’ll add some better shots when I can!

IMG_0050