Dear all,

I'm a complete newbie as far as quantitative finance is concerned (I have no educational background in finance, coding, or science in general). However, I am an avid investor who truly believes in QuantConnect's vision and mission to transition humanity to the future of trading. As such, I will regularly post quant strategies here in this post.

Here is my first paper. (Comment enabled! Feel free to drop some comments in this doc!)

Method: Price Earnings Anomaly

from QuantConnect.Data.UniverseSelection import *
import math
import numpy as np
import pandas as pd
import scipy as sp

class PriceEarningsAnamoly(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2015, 5, 1)   
        self.SetEndDate(2021, 5, 1)         
        self.SetCash(1000000)
        self.SetBenchmark("SPY")
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.symbols = []
        
        # record the year that have passed since the algorithm starts
        self.year = -1
        self._NumCoarseStocks = 200
        self._NumStocksInPortfolio = 10
        
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        
        
    def CoarseSelectionFunction(self, coarse):
        
        if self.Time.year == self.year:
            return self.symbols
        
        # drop stocks which have no fundamental data or have low price
        CoarseWithFundamental = [x for x in coarse if x.HasFundamentalData and x.Price > 5]
        sortedByDollarVolume = sorted(CoarseWithFundamental, key=lambda x: x.DollarVolume, reverse=False) 
        
        return [i.Symbol for i in sortedByDollarVolume[:self._NumCoarseStocks]]

    def FineSelectionFunction(self, fine):
        
        if self.Time.year == self.year:
            return self.symbols
        
        self.year = self.Time.year
        
        fine = [x for x in fine if x.ValuationRatios.PERatio > 0]
        sortedPERatio = sorted(fine, key=lambda x: x.ValuationRatios.PERatio)

        self.symbols = [i.Symbol for i in sortedPERatio[:self._NumStocksInPortfolio]]
        
        return self.symbols
    
    def OnSecuritiesChanged(self, change):
        
        # liquidate securities that removed from the universe
        for security in change.RemovedSecurities:
            if self.Portfolio[security.Symbol].Invested:
                self.Liquidate(security.Symbol)

        count = len(change.AddedSecurities)

        # evenly invest on securities that newly added to the universe
        for security in change.AddedSecurities:
            self.SetHoldings(security.Symbol, 1.0/count)

 

Method:  Small Capitalization Stocks Premium Anomaly

class SmallCapInvestmentAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2015, 5, 1)
        self.SetEndDate(2021, 5, 1)
        self.SetCash(500000)
        self.SetBenchmark("SPY")

        self.year = -1
        self.count = 10

        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)


    def CoarseSelectionFunction(self, coarse):
        ''' Drop stocks which have no fundamental data or have low price '''
        if self.year == self.Time.year:
            return Universe.Unchanged

        return [x.Symbol for x in coarse if x.HasFundamentalData and x.Price > 5]
 
 
    def FineSelectionFunction(self, fine):
        ''' Selects the stocks by lowest market cap '''
        sorted_market_cap = sorted([x for x in fine if x.MarketCap > 0],
            key=lambda x: x.MarketCap)

        return [x.Symbol for x in sorted_market_cap[:self.count]]


    def OnData(self, data):

        if self.year == self.Time.year:
            return

        self.year = self.Time.year

        for symbol in self.ActiveSecurities.Keys:
            self.SetHoldings(symbol, 1/self.count)


    def OnSecuritiesChanged(self, changes):
        ''' Liquidate the securities that were removed from the universe '''
        for security in changes.RemovedSecurities:
            symbol = security.Symbol
            if self.Portfolio[symbol].Invested:
                self.Liquidate(symbol, 'Removed from Universe')

 

Method:  Combination Strategy

from QuantConnect.Data.UniverseSelection import *
import math
import numpy as np
import pandas as pd
import scipy as sp

class PriceEarningsAnamoly(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2015, 5, 1)   
        self.SetEndDate(2021, 5, 1)         
        self.SetCash(500000)
        self.SetBenchmark("SPY")
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.symbols = []
        
        # record the year that have passed since the algorithm starts
        self.year = -1
        self._NumCoarseStocks = 200
        self._NumStocksInPortfolio = 10
        
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        
        
    def CoarseSelectionFunction(self, coarse):
        
        if self.Time.year == self.year:
            return self.symbols
        
        # drop stocks which have no fundamental data or have low price
        CoarseWithFundamental = [x for x in coarse if x.HasFundamentalData and x.Price > 5]
        sortedByDollarVolume = sorted(CoarseWithFundamental, key=lambda x: x.DollarVolume, reverse=False) 
        
        return [i.Symbol for i in sortedByDollarVolume[:self._NumCoarseStocks]]

    def FineSelectionFunction(self, fine):
        
        if self.Time.year == self.year:
            return self.symbols
        
        self.year = self.Time.year
        
        fine = [x for x in fine if x.ValuationRatios.PERatio > 0]
        sortedPERatio = sorted(fine, key=lambda x: x.ValuationRatios.PERatio)

        self.symbols = [i.Symbol for i in sortedPERatio[:self._NumStocksInPortfolio]]
        
        return self.symbols
    
    def OnSecuritiesChanged(self, change):
        
        # liquidate securities that removed from the universe
        for security in change.RemovedSecurities:
            if self.Portfolio[security.Symbol].Invested:
                self.Liquidate(security.Symbol)

        count = len(change.AddedSecurities)

        # evenly invest on securities that newly added to the universe
        for security in change.AddedSecurities:
            self.SetHoldings(security.Symbol, 1.0/count)
            
class SmallCapInvestmentAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2015, 5, 1)
        self.SetEndDate(2021, 5, 1)
        self.SetCash(500000)
        self.SetBenchmark("SPY")

        self.year = -1
        self.count = 10

        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)


    def CoarseSelectionFunction(self, coarse):
        ''' Drop stocks which have no fundamental data or have low price '''
        if self.year == self.Time.year:
            return Universe.Unchanged

        return [x.Symbol for x in coarse if x.HasFundamentalData and x.Price > 5]
 
 
    def FineSelectionFunction(self, fine):
        ''' Selects the stocks by lowest market cap '''
        sorted_market_cap = sorted([x for x in fine if x.MarketCap > 0],
            key=lambda x: x.MarketCap)

        return [x.Symbol for x in sorted_market_cap[:self.count]]


    def OnData(self, data):

        if self.year == self.Time.year:
            return

        self.year = self.Time.year

        for symbol in self.ActiveSecurities.Keys:
            self.SetHoldings(symbol, 1/self.count)


    def OnSecuritiesChanged(self, changes):
        ''' Liquidate the securities that were removed from the universe '''
        for security in changes.RemovedSecurities:
            symbol = security.Symbol
            if self.Portfolio[symbol].Invested:
                self.Liquidate(symbol, 'Removed from Universe')

As you can see, I referenced Quant tutorials heavily, as I didn't know where else to start, and the combination strategy looks really ugly. I don't even know if my logic is correct.

What do you think about the paper? Are there any flaws in my reasoning / code? How could I further optimize it?

Thank you for your time and patience. I'm willing to learn from all of you, and let's develop the best strategy together!