Overall Statistics
Total Trades
485
Average Win
1.51%
Average Loss
-1.02%
Compounding Annual Return
21.128%
Drawdown
41.600%
Expectancy
0.629
Net Profit
276.995%
Sharpe Ratio
0.86
Probabilistic Sharpe Ratio
26.118%
Loss Rate
34%
Win Rate
66%
Profit-Loss Ratio
1.48
Alpha
0.073
Beta
0.829
Annual Standard Deviation
0.188
Annual Variance
0.035
Information Ratio
0.376
Tracking Error
0.146
Treynor Ratio
0.195
Total Fees
$2655.35
Estimated Strategy Capacity
$110000.00
Lowest Capacity Asset
ACH SAG21CX51085
# region imports
from AlgorithmImports import *
# endregion

class MuscularRedOrangeWhale(QCAlgorithm):

    def Initialize(self): # 
        self.SetStartDate(2015, 1 ,1)
        self.SetEndDate(2021, 12, 1)
        self.SetCash(100000)
        self.UniverseSettings.Resolution = Resolution.Daily
        self.UniverseSettings.Leverage = 10.0
        self.AddUniverse(self.CoarseSelection,self.FineSelection)
        self.selected = []
        self.selection_flag = 0
        self.symbol = self.AddEquity('SPY',Resolution.Daily)
        self.Schedule.On(self.DateRules.MonthEnd(self.symbol.Symbol),
                 self.TimeRules.AfterMarketOpen(self.symbol.Symbol),
                 self.time_check)
        self.SetBenchmark('SPY')

    def CoarseSelection(self,coarse):
        if self.selection_flag != 6:
            return Universe.Unchanged 

        selected = [c for c in coarse if c.HasFundamentalData]


        pb_sorted = sorted(selected, key= lambda c: c.DollarVolume ,reverse= True)


        pb = [x.Symbol for x in pb_sorted]


        return pb[:5000]
    
    def  FineSelection(self, fine):

        pb_checked = [c for c in fine if c.ValuationRatios.PBRatio < 1 and c.ValuationRatios.PBRatio > 0]

        filtered_fine = [x for x in fine if x.FinancialStatements.IncomeStatement.NetIncome.TwelveMonths and
                            x.FinancialStatements.CashFlowStatement.CashFlowFromContinuingOperatingActivities.TwelveMonths and
                            x.OperationRatios.ROA.ThreeMonths and x.OperationRatios.ROA.OneYear and
                            x.FinancialStatements.BalanceSheet.ShareIssued.ThreeMonths and x.FinancialStatements.BalanceSheet.ShareIssued.TwelveMonths and
                            x.OperationRatios.GrossMargin.ThreeMonths and x.OperationRatios.GrossMargin.OneYear and
                            x.OperationRatios.LongTermDebtEquityRatio.ThreeMonths and x.OperationRatios.LongTermDebtEquityRatio.OneYear and 
                            x.OperationRatios.CurrentRatio.ThreeMonths and x.OperationRatios.CurrentRatio.OneYear and 
                            x.OperationRatios.AssetsTurnover.ThreeMonths and x.OperationRatios.AssetsTurnover.OneYear and x.ValuationRatios.NormalizedPERatio] 
        sortedByfactor1 = [x for x in filtered_fine if FScore(x.FinancialStatements.IncomeStatement.NetIncome.TwelveMonths,
                            x.FinancialStatements.CashFlowStatement.CashFlowFromContinuingOperatingActivities.TwelveMonths,
                            x.OperationRatios.ROA.ThreeMonths, x.OperationRatios.ROA.OneYear,
                            x.FinancialStatements.BalanceSheet.ShareIssued.ThreeMonths, x.FinancialStatements.BalanceSheet.ShareIssued.TwelveMonths,  
                            x.OperationRatios.GrossMargin.ThreeMonths, x.OperationRatios.GrossMargin.OneYear,
                            x.OperationRatios.LongTermDebtEquityRatio.ThreeMonths, x.OperationRatios.LongTermDebtEquityRatio.OneYear,
                            x.OperationRatios.CurrentRatio.ThreeMonths, x.OperationRatios.CurrentRatio.OneYear,
                            x.OperationRatios.AssetsTurnover.ThreeMonths, x.OperationRatios.AssetsTurnover.OneYear).ObjectiveScore() > 7]
        self.sortedbyfactor = [x.Symbol for x in sortedByfactor1] 

        return self.sortedbyfactor

    def OnData(self, data):
        if self.selection_flag != 6:
            return
        self.selection_flag = 0

        self.Liquidate()

        for symbol in self.sortedbyfactor:
            self.SetHoldings(symbol, 1 / len(self.sortedbyfactor))

        self.sortedbyfactor.clear()
    
    
    def time_check(self):
        self.selection_flag = self.selection_flag + 1


class FScore(object):
    def __init__(self, netincome, operating_cashflow, roa_current,
                 roa_past, issued_current, issued_past, grossm_current, grossm_past,
                 longterm_current, longterm_past, curratio_current, curratio_past,
                 assetturn_current, assetturn_past):
        self.netincome = netincome
        self.operating_cashflow = operating_cashflow
        self.roa_current = roa_current
        self.roa_past = roa_past
        self.issued_current = issued_current
        self.issued_past = issued_past
        self.grossm_current = grossm_current
        self.grossm_past = grossm_past
        self.longterm_current = longterm_current
        self.longterm_past = longterm_past
        self.curratio_current = curratio_current
        self.curratio_past = curratio_past
        self.assetturn_current = assetturn_current
        self.assetturn_past = assetturn_past

    def ObjectiveScore(self):
        fscore = 0
        fscore += np.where(self.netincome > 0, 1, 0)
        fscore += np.where(self.operating_cashflow > 0, 1, 0)
        fscore += np.where(self.roa_current > self.roa_past, 1, 0)
        fscore += np.where(self.operating_cashflow > self.roa_current, 1, 0)
        fscore += np.where(self.longterm_current <= self.longterm_past, 1, 0)
        fscore += np.where(self.curratio_current >= self.curratio_past, 1, 0)
        fscore += np.where(self.issued_current <= self.issued_past, 1, 0)
        fscore += np.where(self.grossm_current >= self.grossm_past, 1, 0)
        fscore += np.where(self.assetturn_current >= self.assetturn_past, 1, 0)
        return fscore