Overall Statistics
Total Trades
1250
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
-30.420%
Drawdown
2.000%
Expectancy
-0.812
Net Profit
-1.479%
Sharpe Ratio
-1.001
Probabilistic Sharpe Ratio
31.887%
Loss Rate
91%
Win Rate
9%
Profit-Loss Ratio
1.14
Alpha
0.004
Beta
0.787
Annual Standard Deviation
0.078
Annual Variance
0.006
Information Ratio
1.007
Tracking Error
0.026
Treynor Ratio
-0.099
Total Fees
$1250.00
Estimated Strategy Capacity
$200000.00
Lowest Capacity Asset
OGCP VKET4LM50ZFP
from datetime import timedelta

from AlgorithmImports import (
    QCAlgorithm,
    Resolution,
    DataNormalizationMode,
    FineFundamentalUniverseSelectionModel,
    AlphaModel,
    ConstantAlphaModel,
    InsightType,
    InsightDirection,
    EqualWeightingPortfolioConstructionModel,
    ImmediateExecutionModel,
    NullRiskManagementModel,
    OrderStatus
)


class Quantmomentum(QCAlgorithm):

    def Initialize(self):
        self.UniverseSettings.Resolution = Resolution.Daily
        self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
        self.SetStartDate(2014, 3, 25)  # Set Start Date
        self.SetEndDate(2014, 4, 8)  # Set End Date
        
        self.SetCash(100000)  # Set Strategy Cash

        self.SetUniverseSelection(nyse_marketcap_breakpoint(self, percentile=0.4, debug=True))
        self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(days=1)))
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(Resolution.Daily))
        self.SetExecution(ImmediateExecutionModel())
        self.SetRiskManagement(NullRiskManagementModel())
        

    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status == OrderStatus.Filled:
            self.Debug("Purchased Stock: {0}".format(orderEvent.Symbol))


def nyse_marketcap_breakpoint(algorithm, *, percentile, nyse_codes=None, debug=False):

    if nyse_codes is None:
        nyse_codes = {'NYS'}

    def in_breakpoint_universe(fine_object):
        return (fine_object.SecurityReference.ExchangeId in nyse_codes and  # NYSE only
                fine_object.SecurityReference.SecurityType == 'ST00000001' and  # Common shares
                fine_object.SecurityReference.CommonShareSubType == None)  # Only ordinary common shares.

    def set_coarse(coarse):
        all_coarse = [s for s in coarse]
        coarse_universe = [s.Symbol for s in all_coarse if s.HasFundamentalData]
        if debug:
            algorithm.Debug(f'Coarse universe ({algorithm.Time}) - {len(all_coarse)} coarse objects received, {len(coarse_universe)} with fundamentals.')
        return coarse_universe
    
    def set_fine(fine):
        all_fine = [s for s in fine]
        breakpoint_stocks = [s for s in all_fine if in_breakpoint_universe(s)]
        breakpoint_stocks.sort(key=lambda s: s.MarketCap)
        if breakpoint_stocks:
            index = int(len(breakpoint_stocks) * percentile)
            breakpoint_security = breakpoint_stocks[index]
            breakpoint = breakpoint_security.MarketCap
        else:
            breakpoint = 1e9
        fine_universe = [f.Symbol for f in all_fine if f.MarketCap >= breakpoint]
        if debug:
            algorithm.Debug(f'Fine universe ({algorithm.Time}) - {len(all_fine)} fine objects received, {len(breakpoint_stocks)} in NYSE universe, {breakpoint} is breakpoint, resulting in {len(fine_universe)} in fine universe.')
        return fine_universe

    return FineFundamentalUniverseSelectionModel(set_coarse, set_fine)


class QuantitativeMomentum(AlphaModel):

    def __init__(self, lookback_mo=12, reversal_mo=1, momentum_pct=0.1, quality_pct=0.5):
        self.lookback_mo = lookback_mo
        self.reversal_mo = reversal_mo
        self.momentum_pct = momentum_pct
        self.quality_pct = quality_pct
    
    def Update(self, algorithm, data):
        pass
        # Update all momentum for new data
        # Figure out NYSE breakpoint

    def OnSecuritiesChanged(self, algorithm, changes):
        pass
        # For all added securities: add a quantitative momentum data object
        # Prepopulate it with up to 252 days of trading data
        # For all removed securities: delete it.