Overall Statistics
Total Orders
47187
Average Win
0.01%
Average Loss
-0.01%
Compounding Annual Return
-5.907%
Drawdown
35.900%
Expectancy
-0.104
Start Equity
10000000
End Equity
7116915.22
Net Profit
-28.831%
Sharpe Ratio
-0.841
Sortino Ratio
-0.812
Probabilistic Sharpe Ratio
0.000%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
0.66
Alpha
-0.051
Beta
-0.061
Annual Standard Deviation
0.066
Annual Variance
0.004
Information Ratio
-0.715
Tracking Error
0.175
Treynor Ratio
0.912
Total Fees
$116954.12
Estimated Strategy Capacity
$11000.00
Lowest Capacity Asset
QES WRXDDCLORBS5
Portfolio Turnover
0.80%
#region imports
from AlgorithmImports import *
#endregion
# https://quantpedia.com/Screener/Details/199


class ROAEffectWithinStocks(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2015, 1, 1)  
        self.set_end_date(2020, 8, 1)  
        self.set_cash(10000000) 
        self.set_security_initializer(BrokerageModelSecurityInitializer(
            self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        self.settings.minimum_order_margin_portfolio_percentage = 0
        self.universe_settings.resolution = Resolution.DAILY
        self.add_universe(self._coarse_selection_function, self._fine_selection_function)
        self.add_equity("SPY", Resolution.DAILY)
        
        self.schedule.on(self.date_rules.month_start("SPY"), self.time_rules.after_market_open("SPY"), self._rebalance)
       
        self._monthly_rebalance = False 
        self._coarse = False
        
    def _coarse_selection_function(self, coarse):
        if self._monthly_rebalance:
            self._coarse = True
            return [x.symbol for x in coarse if x.has_fundamental_data]
        return Universe.UNCHANGED
    
    def _fine_selection_function(self, fine):
        if self._monthly_rebalance:
            fine =[i for i in fine if i.earning_reports.basic_average_shares.three_months != 0
                                  and i.earning_reports.basic_eps.twelve_months != 0
                                  and i.valuation_ratios.pe_ratio != 0
                                  # sales is greater than 10 million
                                  and i.valuation_ratios.sales_per_share*i.earning_reports.diluted_average_shares.value > 10000000
                                  and i.operation_ratios.roa.value != 0]
            
            # sort into 2 halfs based on market capitalization
            sorted_market_cap = sorted(fine, key=lambda x: x.market_cap, reverse=True)
            top = sorted_market_cap[:int(len(sorted_market_cap)*0.5)]
            bottom = sorted_market_cap[-int(len(sorted_market_cap)*0.5):]
            
            # each half is then divided into deciles based on Return on Assets (ROA)
            sorted_top_by_roa = sorted(top, key=lambda x: x.operation_ratios.roa.value, reverse=True)
            sorted_bottom_by_roa = sorted(bottom, key=lambda x: x.operation_ratios.roa.value, reverse=True)
            
            # long top decile from each market capitalization group 
            long_ = sorted_top_by_roa[:int(len(sorted_top_by_roa)*0.1)] + sorted_bottom_by_roa[:int(len(sorted_top_by_roa)*0.1)]
            self._long_stocks = [i.symbol for i in long_]
            
            # short bottom decile from each market capitalization group 
            short = sorted_top_by_roa[-int(len(sorted_top_by_roa)*0.1):] + sorted_bottom_by_roa[-int(len(sorted_top_by_roa)*0.1):]
            self._short_stocks = [i.symbol for i in short]
            
            return self._long_stocks + self._short_stocks
        else:
            return Universe.UNCHANGED
    
    def _rebalance(self):
        self._monthly_rebalance = True
            
    def on_data(self, data):
        if not (self._monthly_rebalance and self._coarse): 
            return

        self._coarse = False
        self._monthly_rebalance = False

        long_stocks = [x for x in self._long_stocks if self.securities[x].price]
        long_weight = 0.5 / len(long_stocks)
        targets = [PortfolioTarget(x, long_weight) for x in long_stocks]

        short_stocks = [x for x in self._short_stocks if self.securities[x].price]
        short_weight = -0.5 / len(short_stocks)
        targets.extend([PortfolioTarget(x, short_weight) for x in short_stocks])

        self.set_holdings(targets, True)