Overall Statistics
Total Orders
1415
Average Win
0.21%
Average Loss
-0.20%
Compounding Annual Return
12.205%
Drawdown
26.500%
Expectancy
0.485
Start Equity
1000000
End Equity
2394834.50
Net Profit
139.483%
Sharpe Ratio
0.608
Sortino Ratio
0.628
Probabilistic Sharpe Ratio
12.670%
Loss Rate
27%
Win Rate
73%
Profit-Loss Ratio
1.04
Alpha
0.005
Beta
0.867
Annual Standard Deviation
0.137
Annual Variance
0.019
Information Ratio
-0.077
Tracking Error
0.091
Treynor Ratio
0.096
Total Fees
$12582.94
Estimated Strategy Capacity
$530000.00
Lowest Capacity Asset
AJX VY25795D7GIT
Portfolio Turnover
0.73%
#region imports
from AlgorithmImports import *
#endregion


class MomentumInREIT(QCAlgorithm):

    def initialize(self):
        #rebalancing should occur in July
        self.set_start_date(2010, 12, 15)
        self.set_end_date(2018, 7, 15)
        self.set_cash(1000000)
        self.universe_settings.resolution = Resolution.DAILY
        self._filtered_fine = None
        self.add_universe(self._coarse_selection_function, self._fine_selection_function)
        self.add_equity("SPY", Resolution.DAILY)
        #monthly scheduled event
        self.schedule.on(self.date_rules.month_start("SPY"), self.time_rules.at(23, 0), self._rebalance)
        self._months = -1
        self._quarterly_rebalance = False
        
    def _coarse_selection_function(self, coarse):
        if self._quarterly_rebalance:
            # drops penny stocks and stocks with no fundamental data
            return [x.symbol for x in coarse if float(x.price) > 1 and x.has_fundamental_data and float(x.volume) > 10000]
        else: 
            return []      
    
    def _fine_selection_function(self, fine):
        if self._quarterly_rebalance:
            #filters out the companies that are not REITs
            fine = [x for x in fine if x.company_reference.is_reit] 
            
            #calculating the 11 month (1-month lagged) returns 
            start = self.time - timedelta(365)
            end = self.time - timedelta(30)
            for x in fine:
                hist = self.history([x.symbol], start, end, Resolution.DAILY)
                if not hist.empty:
                    start_price = hist["close"].iloc[0]
                    end_price = hist["close"].iloc[-1]
                    x.momentum = (end_price - start_price) / start_price
            
            fine = [x for x in fine if hasattr(x, 'momentum')]
            #we sort REITs based on their returns
            sorted_filter = sorted(fine, key=lambda x: x.momentum)
            self._filtered_fine = [i.symbol for i in sorted_filter]
            return self._filtered_fine
        else:
            return []
    
    def _rebalance(self):
        #quarterly rebalance
        self._months += 1
        if self._months % 3 == 0:
            self._quarterly_rebalance = True

    def on_data(self, data):
        if not self._quarterly_rebalance: 
            return 
        if self._filtered_fine:
            portfolio_size = int(len(self._filtered_fine)/3)
            #pick the upper trecile to short and the lower decile to long
            long_stocks = self._filtered_fine[-portfolio_size:]
            stocks_invested = [x.key for x in self.portfolio]
            for i in stocks_invested:
                #liquidate the stocks not in our filtered_fine list
                if i not in long_stocks:
                    self.liquidate(i) 
                #long the stocks in the list
                elif i in long_stocks:
                    self.set_holdings(i, 1/(portfolio_size))
            self._quarterly_rebalance = False
            self._filtered_fine = None