Overall Statistics
Total Orders
3297
Average Win
0.28%
Average Loss
-0.38%
Compounding Annual Return
8.539%
Drawdown
41.700%
Expectancy
0.105
Start Equity
50000
End Equity
99001.76
Net Profit
98.004%
Sharpe Ratio
0.361
Sortino Ratio
0.426
Probabilistic Sharpe Ratio
1.712%
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
0.74
Alpha
-0.043
Beta
1.085
Annual Standard Deviation
0.182
Annual Variance
0.033
Information Ratio
-0.342
Tracking Error
0.1
Treynor Ratio
0.061
Total Fees
$4492.70
Estimated Strategy Capacity
$2100000.00
Lowest Capacity Asset
TTD WE3561IA1KKL
Portfolio Turnover
4.53%
# region imports
from AlgorithmImports import *
# endregion


class StockSelectionStrategyBasedOnFundamentalFactorsAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2009, 1, 2)
        self.set_end_date(2017, 5, 2)
        self.set_cash(50000)

        self._current_month = -1
        self._coarse_count = 300
        self._fine_count = 10

        self.add_universe(self._coarse_selection_function, self._fine_selection_function)

        self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(30)))

        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(lambda _: None))

    def _coarse_selection_function(self, coarse):
        if self._current_month == self.time.month:
            return Universe.UNCHANGED

        self._current_month = self.time.month

        sorted_by_dollar_volume = sorted([x for x in coarse if x.has_fundamental_data],
            key=lambda x: x.dollar_volume, reverse=True)[:self._coarse_count] 

        return [i.symbol for i in sorted_by_dollar_volume]

    def _fine_selection_function(self, fine):
        fine = [x for x in fine if x.earning_reports.total_dividend_per_share.three_months
                               and x.valuation_ratios.price_change_1m 
                               and x.valuation_ratios.book_value_per_share
                               and x.valuation_ratios.fcf_yield]

        sorted_by_factor1 = sorted(fine, key=lambda x: x.earning_reports.total_dividend_per_share.three_months, reverse=True)
        sorted_by_factor2 = sorted(fine, key=lambda x: x.valuation_ratios.price_change_1m, reverse=False)
        sorted_by_factor3 = sorted(fine, key=lambda x: x.valuation_ratios.book_value_per_share, reverse=True)
        sorted_by_factor4 = sorted(fine, key=lambda x: x.valuation_ratios.fcf_yield, reverse=True)

        stock_dict = {}

        for rank1, ele in enumerate(sorted_by_factor1):
            rank2 = sorted_by_factor2.index(ele)
            rank3 = sorted_by_factor3.index(ele)
            rank4 = sorted_by_factor4.index(ele)
            stock_dict[ele] = rank1 + rank2 + rank3 + rank4

        sorted_stock = sorted(stock_dict.items(),
            key=lambda d:d[1], reverse=True)[:self._fine_count]

        return [x[0].symbol for x in sorted_stock]