Back

Strategy - Defensive Adaptive Asset Allocation using the Algorithm Framework (+ Research Notebook)

Hi All,

I'm sharing my implementation of this interesting strategy on "Defensive Adaptive Asset Allocation" by Ilya Kipnis, which you can find explained in this link https://quantstrattrader.wordpress.com/2019/01/24/right-now-its-kda-asset-allocation/

The link above offers a more detailed explanation of the logic and its origin, but in QC lingo it can be summarised as follows:

  • UNIVERSE: The universe consists of ETFs from different asset classes: SPY (US equities), VGK (European equities),  EWJ  (Japanese equities), EEM (Emerging market equities), VNQ (US REITs), RWX (International REITs), TLT (US 30-year Treasuries), IEF (US 10-year Treasuries), DBC (Commodities) and GLD (Gold).
  • ALPHA: Every N days, we compute the 1-3-6-12 momentum filter for the whole Universe (i.e. the sum of 12 * 1-month momentum, 4 * 3-month momentum, 2 * 6-month momentum and 12-month momentum) and rank them. The final selection is based on Dual Momentum: a combination of Positive Absolute Momentum (momentum score above 0) and Relative Momentum (keeping the Top N assets in the ranking). In the original strategy, the proposed rebalancing period is monthly. However, this type of strategies are very sensitive to timing (when you launch the strategy) and rebalancing at the end or start of each month can have a huge impact on results. In my implementation, I'm proposing using a parameter daysToRecalculate = 21 to select the number of trading days to perform the momentum calculations and rebalancing regardless of when the strategy is deployed, thus reducing the timing biais.
  • PORTFOLIO: This is where things get more interesting:
    • We apply portfolio optimization to the top assets in a particular way. We compute the covariance matrix using one-month volatility estimates, and a correlation matrix that is the weighted average of the same parameters used for the momentum filter (12 * 1-month correlation + 4 * 3-month correlation + 2 * 6-month correlation + 12-month correlation, all divided by 19). In the original strategy, the proposed optimization is Minimum Variance. However, in my implementation I am adding the possibility to choose between: Minimize Portfolio Variance, Maximize Portfolio Return and Maximize Portfolio Sharpe Ratio.
    • This strategy also uses two Canary Assets: VWO (Vanguard FTSE Emerging Markets ETF) and BND (Vanguard Total Bond Market ETF), to determine the level of exposure to "risky assets". We compute the 1-3-6-12 momentum for the Canary Assets and our exposure to "risky assets" will be:
      • 100% if both have Positive Absolute Momentum.
      • 50% if only one has Positive Absolute Momentum.
      • 0% if none has Positive Absolute Momentum.
      • The remaining % from the above calculation will go to IEF if this asset also has Positive Absolute Momentum. Otherwise, we stay in cash.
  • EXECUTION: Immediate execution with Market Orders.
  • RISK: No extra risk management.

In order to illustrate a few interesting things about the algorithm, I also wrote a Research file. In the research.ipynb file you can find:

  • A step by step walk-through of the Momentum Score calculation.
  • A step by step walk-through on how to calculate the Covariance Matrix using a custom correlation matrix (like the one in our algorithm, computed as a weighted average of multiple period correlations).

Enjoy!

Emilio

InnoQuantivity.com

Update Backtest








0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Thank you that's great work to share!!

0

Thanks Remy!

Glad you find it useful :)

Emilio

InnoQuantivity.com

0

Excellent - thank you for sharing.

0

How would I go about adjusting this to be based off calendar month instead of 21 trading days?

I would like to check the previous month momentum and make trade adjustments on the 1st trading day of each month.

0

Hi Ryan!

I'm glad you found the algo interesting. In answer to your reuest, I'm attaching a new implementation with the following new features (which you can find in the main.py script):

  • In the main.py script, you can find the parameter rebalancingPeriod to select date rules (for the first trading day of period): Expiry.EndOfDay, Expiry.EndOfWeek, Expiry.EndOfMonth, Expiry.EndOfQuarter, Expiry.EndOfYear. Basically, now you can select different date rules to trigger the recalculation of momentum and portfolio rebalancing.
  • Chart Optimal Weights %: I added the time series of optimal weights for each asset at every rebalancing period. Look for the chart in the Select Chart box in the top right. Also, you can click on the Ticker label to add/remove the series for better visualization.

It's already interesting to see how well it performed in Q1 2020: +5% between 2020-01-01 and 2020-04-07. Have a look at the weights in the chart, almost only invested in IEF and GLD since 1st of February...that was a good catch!

Thanks and I hope you can share some of your findings!

Emilio

InnoQuantivity.com

1


Hi Emilio,

very good job!

I am studying this strategy, wich likes promissing, and also your code.

For the crash protection, I´m experiencing a 2x leveraged ETF, UST to see if I can improve return.

I would like to place some questions to you:

1 - The strategy also considers short positions? I saw some short trades in the log.

2 - As I am not yet using algo trading, For forward testing I'm following the portfolio rebalancing mannualy using the log info, but I am getting the results of the EndofWeek only in the next week's Monday when the market is open, or when using EndofDay, the results only appear when backtesting on the open of the following day. Is that possible to have this info after the closing instead of the next period?

1

Hi André,

Looking at the Insights Summary on the backtest view page, we can see the strategy only emits insights with an up direction. Thus, no short positions are taken.

We can adjust the algorithm to log the portfolio rebalancing info at the last close of the month by adjusting our universe resolution to hourly data. This will ensure we get the closing price before midnight.

self.UniverseSettings.Resolution = Resolution.Hour

Next, we need to replace the rebalacingPeriod function to return the datetime of the last close of the month.

def rebalancingPeriod(time):
month_end = Expiry.EndOfMonth(time)
while True:
# Find last trading day of the current month
days_behind = 1
while True:
last_trading_day = month_end - timedelta(days=days_behind)
hours = self.Securities["SPY"].Exchange.Hours.GetMarketHours(last_trading_day)
if hours.IsClosedAllDay:
days_behind += 1
continue
break

# Set rebalance to be the close of the last trading day
hours_str = hours.ToString()
idx = hours_str.index(" Market:")
close_time = hours_str[idx + 18:idx + 26]
hour = int(close_time[:2])
minute = int(close_time[3:5])
last_close_month = last_trading_day.replace(hour=hour, minute=minute)

# If last month close is at or before current algo time, move to next month
if time >= last_close_month:
month_end = Expiry.EndOfMonth(month_end)
continue
break
return last_close_month

Finally, we need to alter our calls to gather historical pricing for the symbols. Instead of requesting the previous 253 days, we can request the previous 252 full trading days and gather the current closing price by requesting the close of the previous hour.

See the attached backtest for the full solution files. Notice how the backtest isn't exactly the same as the previous algorithm. This is a result of a missing data error in the original algorithm that does not occur in the new algorithm.

Best,
Derek

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Brilliant, thanks Derek Melchin !!

Emilio

InnoQuantivity.com

0

Emilio, Thanks for sharing. I am new and studying your code.

Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), 0.25)

Would you mind breakdown this code for me? I copied and pasted to the research notebook but it does not spit out any numbers. 

Thanks in advance

0

Hi Juhwan Kim ,

Thanks for your message.

So that line simply returns a time object equivalent to 6 hours (24h * 0.25), which will be the expiration for all the insights generated (as you can see further down when the insights are created with insight expiry). There are other ways of doing the same (I believe using timedelta(hours=6) would be equivalent), but the above code allows you to create that expiration based on the resolution of the algo so it's more flexible in that sense. If you use Resolution.Hour, then the insight expiration would be 15 minutes (60 minutes * 0.25).

The whole point of using 0.25, and not 0.5 or 1 or any other, is to make sure the insight expires at the next iteration so we rely on the constant generation of new insights at every iteration to maintain the positions. This is just specific to the design of this algo and does not need to be like this.

Hope it helps!

Emilio

InnoQuantivity.com

 

0

Dear Emilio,

Thanks for the explanation. it helped a lot to understand. I am very excited by your shared codes.

P.S. I also used str function and was able to see the output :)

Have a good day!

0

That's great to hear Juhwan Kim !

Happy to help :)

Emilio

InnoQuantivity.com

0

Update Backtest





0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Loading...

This discussion is closed