Overall Statistics
Total Trades
457
Average Win
2.21%
Average Loss
-2.14%
Compounding Annual Return
8.668%
Drawdown
48.000%
Expectancy
0.314
Net Profit
323.510%
Sharpe Ratio
0.475
Probabilistic Sharpe Ratio
0.347%
Loss Rate
35%
Win Rate
65%
Profit-Loss Ratio
1.03
Alpha
0.011
Beta
0.775
Annual Standard Deviation
0.149
Annual Variance
0.022
Information Ratio
-0.071
Tracking Error
0.087
Treynor Ratio
0.092
Total Fees
$1268.00
Estimated Strategy Capacity
$78000000.00
Lowest Capacity Asset
XLB RGRPZX100F39
Portfolio Turnover
2.40%
import numpy as np
import pandas as pd
from datetime import datetime

from AlgorithmImports import *

class EmmausAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2006, 1, 1)
        self.SetCash(30000) 

        # choose 11 sector ETFs
        tickers = [ "XLY",  # Materials
                    "XLB",  # Communication Services
                    "XLE",  # Energy
                    "XLF",  # Financials
                    "XLK",  # Technology
                    "XLP",  # Staples
                    "IYR", # Real Estate
                    "XLU",  # Utilities
                    "XLV",  # Health Care
                    "XLT",
                    ]  # Discretionary 

        self.data = {}

        for ticker in tickers:
            symbol = self.AddEquity(ticker, Resolution.Daily).Symbol
            self.data[symbol] = {"roc": RateOfChangePercent(3), "dollar_return": None}
            consolidator = TradeBarConsolidator(CalendarType.Monthly)
            self.RegisterIndicator(symbol, self.data[symbol]["roc"], consolidator)

        self.SetWarmUp(30)

        # shcedule the function to fire at the month start 
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday),  self.TimeRules.At(8, 0), self.Rebalance)


    def Rebalance(self):
        if self.IsWarmingUp:
            return
        
        # calculate dollar return for last trading year
        start_date = self.Time.replace(year=self.Time.year-1, month=1, day=1)
        end_date = self.Time.replace(year=self.Time.year-1, month=12, day=31)
        for symbol in self.data:
            history = self.History([symbol], start_date, end_date, Resolution.Daily)
            if not history.empty:
                self.data[symbol]["dollar_return"] = self.Portfolio[symbol].Price / history.iloc[0]["close"] - 1

        # calculate momentum rank based on combination of last 3 months' return and dollar return for last trading year
        momentum_rank = {symbol: 0 for symbol in self.data}
        for symbol, data in self.data.items():
            roc = data["roc"].Current.Value
            dollar_return = data["dollar_return"]
            if dollar_return is None:
                # if no dollar return data, use 0
                dollar_return = 0
            momentum_rank[symbol] = roc + dollar_return

        # select top 3 momentum ETFs
        top3 = sorted(momentum_rank.items(), key=lambda x: x[1], reverse=True)[:3]
        top3_symbols = [symbol for symbol, rank in top3]
        
        for kvp in self.Portfolio:
            symbol = kvp.Key
            if symbol in top3_symbols:
                continue
            # liquidate the security which is no longer in the top3 momentum list
            if kvp.Value.Invested:
                self.Liquidate(symbol, 'Not selected')

        for symbol in top3_symbols:
            if not self.Portfolio[symbol].Invested:
            
              self.SetHoldings(symbol, 1/3)