Overall Statistics
Total Trades
6366
Average Win
0.01%
Average Loss
0.00%
Compounding Annual Return
29.905%
Drawdown
5.200%
Expectancy
0.305
Net Profit
6.897%
Sharpe Ratio
1.323
Probabilistic Sharpe Ratio
54.826%
Loss Rate
43%
Win Rate
57%
Profit-Loss Ratio
1.27
Alpha
-0.272
Beta
0.972
Annual Standard Deviation
0.163
Annual Variance
0.027
Information Ratio
-2.505
Tracking Error
0.114
Treynor Ratio
0.223
Total Fees
$6366.00
Estimated Strategy Capacity
$7400000.00
Lowest Capacity Asset
TSM R735QTJ8XC9X
#region imports
from AlgorithmImports import *
#endregion
class StockSelectionStrategyBasedOnFundamentalFactorsAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2020, 11, 1)  # Set Start Date
        self.SetEndDate(2021, 2, 1)    # Set End Date
        self.SetCash(100000)          # Set Strategy Cash

        self.coarse_count = 10 #Num Equity Fund
        self.fund_count = 30    #Num Equity End
        self.end_count = 10   
        period=21 # for Indikators

        self.nextRebalance = self.Time  #  next balance time
        self.rebalanceDays = 90         # Rebalance quarterly

        self.Settings.RebalancePortfolioOnInsightChanges = False
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(90)))
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time:self.nextRebalance))
        self.Settings.RebalancePortfolioOnInsightChanges = False



    def CoarseSelectionFunction(self, coarse):
        if self.Time < self.nextRebalance: return Universe.Unchanged

        HasFundData = [x for x in coarse if x.HasFundamentalData]
        return [x.Symbol for x in HasFundData]

    def FineSelectionFunction(self, fine):

        if self.Time < self.nextRebalance: return Universe.Unchanged

#Fundamental
        #sorting Fundamental
        sortedByMarketCap = sorted(fine, key=lambda x: x.MarketCap, reverse=True)[:self.coarse_count]
        return [i.Symbol for i in sortedByMarketCap] 

        fine = [x for x in fine if x.ValuationRatios.SustainableGrowthRate >0.0001
                                and x.ValuationRatios.PERatio>0.0001
                                and x.ValuationRatios.FCFYield>0.0001]

        sortedByfactor1 = sorted(fine, key=lambda x: x.ValuationRatios.SustainableGrowthRate, reverse=True)
        sortedByfactor2 = sorted(fine, key=lambda x: x.ValuationRatios.PERatio, reverse=False)
        sortedByfactor3 = sorted(fine, key=lambda x: x.ValuationRatios.FCFYield, reverse=True)

        stock_dict = {}


        #ranking Fundamental
        for rank1, ele in enumerate(sortedByfactor1):
            rank2 = sortedByfactor2.index(ele)
            rank3 = sortedByfactor3.index(ele)
            stock_dict[ele] = rank1 + rank2 + rank3

        fund_sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1], reverse=True)[:self.fund_count]
        return [i.Symbol for i in fund_sorted_stock] 

#technical
        #prep technical filter
        self.SerWarmUp(period)
        for symbol in fund_sorted_stock:
            self.AddEquity(symbol, Resolution.Daily)
            self.dataSMA[symbol] = self.SMA(symbol, period, Resolution.Daily)
            self.dataMom[symbol] = self.MOM(symbol, period, Resolution.Daily)
            price = self.Securities[symbol].Price 
            faktorSMA = price/dataSMA

        #technical filter
        sortedBySMA = sorted(fine, key=lambda x: x.faktorSMA, reverse=True)
        sortedByMom = sorted(fine, key=lambda x: x.dataMom, reverse=False)

        stock_dict = {}

        
        #renking technsicher Filter
        for rank1, ele in enumerate(sortedBySMA):
            rank2 = sortedByMom.index(ele)
            stock_dict[ele] = rank1 + rank2

        sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1], reverse=True)[:self.end_count]
        return [i.Symbol for i in sorted_stock] 




    def rebalance(self):
        # if this month the stock are not going to be long/short, liquidate it.
        for i in self.Portfolio.Values:
            if (i.Invested) and (i not in sorted_stock):
                self.Liquidate(i.Symbol)                

        
    # Assign each stock equally. Alternatively you can design your own portfolio construction method
        for i in sorted_stock:
            self.SetHoldings(i, 0.9/self.end_count)
        #for i in self.short:

        self.nextRebalance += timedelta(self.rebalanceDays)