Overall Statistics
Total Trades
6139
Average Win
0.13%
Average Loss
-0.06%
Compounding Annual Return
9.831%
Drawdown
54.000%
Expectancy
0.285
Net Profit
30.461%
Sharpe Ratio
0.367
Probabilistic Sharpe Ratio
4.551%
Loss Rate
58%
Win Rate
42%
Profit-Loss Ratio
2.07
Alpha
0.283
Beta
-0.012
Annual Standard Deviation
0.768
Annual Variance
0.59
Information Ratio
0.245
Tracking Error
0.777
Treynor Ratio
-23.386
Total Fees
$6378.08
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel

class NetNet(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)  # Set Start Date
        self.SetEndDate(2017, 10, 31)
        self.SetCash(100000)  # Set Strategy Cash
        
        self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction, None, None))
        self.UniverseSettings.Resolution = Resolution.Daily
        
        self.SetAlpha(ConstantAlphaModelSellEOY(InsightType.Price, InsightDirection.Up, timedelta(days=365)))
        
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: None))
        
        self.SetExecution(ImmediateExecutionModel())

        self.SetBenchmark("SPY")
        
        self.year = -1


    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''

        # if not self.Portfolio.Invested:
        #    self.SetHoldings("SPY", 1)


    # on 15 Jan, filter for securities with fundamental data
    def CoarseSelectionFunction(self, coarse):
        if self.Time.year == self.year:
            return Universe.Unchanged
    
        self.year = self.Time.year
        return [ x.Symbol for x in coarse if x.HasFundamentalData ][:1000]
    
    
    # on 15 Jan, filter first for securities with shares and then filter a second time for net net stocks
    def FineSelectionFunction(self, fine):
        filtered = [ x for x in fine if ((x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths - x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths) > 0) and (x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths > 0) ]
        # filtered = [ x for x in filtered if (x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths > 0) ]
        # filtered = [ x for x in filtered if (x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths > 0) ]
        filtered = [ x.Symbol for x in filtered if (x.Price / ((x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths - x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths) / x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths)) <= 0.66 ]
        
        # for x in filtered:
        #     self.Log("Symbol: " + str(x.Symbol))
        #     self.Log("Name: " + x.CompanyReference.LegalName)
        #     self.Log(str(self.Time.month) + str(self.Time.day) + str(self.Time.year))
        #     self.Log("Price: " + str(x.Price))
        #     self.Log("Current Assets: " + str(x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths))
        #     self.Log("Total Liabilities: " + str(x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths))
        #     self.Log("Preferred Stock: " + str(x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths))
        #     self.Log("Shares: " + str(x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths))
        # filtered = [ x.Symbol for x in filtered ]
        
        return filtered[:100]
        
class ConstantAlphaModelSellEOY(AlphaModel):
    ''' Provides an implementation of IAlphaModel that always returns the same insight for each security'''

    def __init__(self, type, direction, period, magnitude = None, confidence = None):
        '''Initializes a new instance of the ConstantAlphaModel class
        Args:
            type: The type of insight
            direction: The direction of the insight
            period: The period over which the insight with come to fruition
            magnitude: The predicted change in magnitude as a +- percentage
            confidence: The confidence in the insight'''
        self.type = type
        self.direction = direction
        self.period = period
        self.magnitude = magnitude
        self.confidence = confidence
        self.securities = []

        typeString = Extensions.GetEnumString(type, InsightType)
        directionString = Extensions.GetEnumString(direction, InsightDirection)

        self.Name = '{}({},{},{}'.format(self.__class__.__name__, typeString, directionString, strfdelta(period))
        if magnitude is not None:
            self.Name += ',{}'.format(magnitude)
        if confidence is not None:
            self.Name += ',{}'.format(confidence)

        self.Name += ')'


    def Update(self, algorithm, data):
        ''' Creates a constant insight for each security as specified via the constructor
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            The new insights generated'''
        insights = []
        
        for security in self.securities:
            # security price could be zero until we get the first data point. e.g. this could happen
            # when adding both forex and equities, we will first get a forex data point
            if security.Price != 0:
                if algorithm.Time.month == 12 and algorithm.Time.day > 20:
                    insights.append(Insight(security.Symbol, self.period, self.type, InsightDirection.Flat, self.magnitude, self.confidence))
                elif not algorithm.Securities[security.Symbol].Invested:
                    insights.append(Insight(security.Symbol, self.period, self.type, self.direction, self.magnitude, self.confidence))

        return insights


    def OnSecuritiesChanged(self, algorithm, changes):
        ''' Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
        for added in changes.AddedSecurities:
            self.securities.append(added)

        # this will allow the insight to be re-sent when the security re-joins the universe
        for removed in changes.RemovedSecurities:
            if removed in self.securities:
                self.securities.remove(removed)
    
def strfdelta(tdelta):
    d = tdelta.days
    h, rem = divmod(tdelta.seconds, 3600)
    m, s = divmod(rem, 60)
    return "{}.{:02d}:{:02d}:{:02d}".format(d,h,m,s)