Overall Statistics
Total Orders
1808
Average Win
0.50%
Average Loss
-0.75%
Compounding Annual Return
19.953%
Drawdown
50.000%
Expectancy
0.136
Start Equity
100000
End Equity
248302.93
Net Profit
148.303%
Sharpe Ratio
0.503
Sortino Ratio
0.603
Probabilistic Sharpe Ratio
12.383%
Loss Rate
32%
Win Rate
68%
Profit-Loss Ratio
0.67
Alpha
0.053
Beta
1.204
Annual Standard Deviation
0.29
Annual Variance
0.084
Information Ratio
0.293
Tracking Error
0.236
Treynor Ratio
0.121
Total Fees
$2870.03
Estimated Strategy Capacity
$4300000.00
Lowest Capacity Asset
HL R735QTJ8XC9X
Portfolio Turnover
5.60%
Drawdown Recovery
1036
# region imports
from AlgorithmImports import *
# endregion

class StockSelectionStrategyBasedOnFundamentalFactorsAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(datetime.now()-timedelta(5*365))
        self.set_cash(100000)

        self.universe_settings.schedule.on(self.date_rules.month_start("SPY"))
        self.add_universe(self._select)

        self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(30)))
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(lambda _: None))

    def _select(self, fundamental):
        top_volume = 300
        top_rank = 10

        fundamental = sorted(
            [
                x for x in fundamental 
                if not any([np.isnan(factor) for factor in [
                    x.valuation_ratios.fcf_yield, 
                    x.valuation_ratios.price_change_1m, 
                    x.valuation_ratios.book_value_per_share, 
                    x.earning_reports.total_dividend_per_share.three_months
                ]])
            ], 
            key=lambda x: x.dollar_volume, reverse=True)[:top_volume]

        sorted_by_factor1 = sorted(fundamental, key=lambda x: x.valuation_ratios.fcf_yield, reverse=True)
        sorted_by_factor2 = sorted(fundamental, key=lambda x: x.valuation_ratios.price_change_1m, reverse=False)
        sorted_by_factor3 = sorted(fundamental, key=lambda x: x.valuation_ratios.book_value_per_share, reverse=True)
        sorted_by_factor4 = sorted(fundamental, key=lambda x: x.earning_reports.total_dividend_per_share.three_months, 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)[:top_rank]

        return [x.symbol for x,_ in sorted_stock]