Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
0
Tracking Error
0
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
from QuantConnect import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *
from QuantConnect.Util import *
from QuantConnect.Interfaces import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Notifications import *
from QuantConnect.Orders import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Orders.Fills import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Scheduling import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Securities.Interfaces import *
from datetime import date, datetime, timedelta
from QuantConnect.Python import *
from QuantConnect.Storage import *
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm
import math
import numpy as np
import pandas as pd
import scipy as sp

class MicroGrowth(QCAlgorithm):

    def Initialize(self):
        #self.SetStartDate(2020, 2, 12)  # Set Start Date
        self.SetStartDate(2019, 1,1)
        self.SetEndDate(2019, 3, 1)
        self.SetCash(100000)  # Set Strategy Cash
        #self.Settings.FreePortfolioValuePercentage = 0.6
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
        self.UniverseSettings.Resolution = Resolution.Hour
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        self.lasttime = None
        self.monthinterval = 1
        self.periods = [30,60,180]
        self.rebalance_interval = timedelta(days = min(self.periods))
        self.tohold = None
        self.tobeliquidated = None
        self.numsecurities = 25
        self.SetWarmUp(timedelta(hours= 4))
        self.debug_execution = False
        self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
        self.history_dict = {}
        self.rebalance = False
        #self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("TSLA", 0), self.ClosingBar)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=10)), self.RebalancePortfolio)
    
    def RebalancePortfolio(self):
        return
        
        if self.IsWarmingUp: return
        if self.rebalance == True:
            self.rebalance = False
            # selected symbols will be found in Log
            self.Debug('\n\n\n'+f'========== NEW CYCLE ==========')
            # self.Debug(f'New Securities Added: {[security.Symbol.Value for security in changes.AddedSecurities]}')
            # self.Debug(f'Securities Removed{[security.Symbol.Value for security in changes.RemovedSecurities]}')
            self.Debug(f'PORTFOLIO CASH BEFORE LIQUIDATION: {self.Portfolio.Cash}')
            self.Debug(f'PORTFOLIO UNSETTLED CASH BEFORE LIQUIDATION: {self.Portfolio.UnsettledCash}')
            for sym in self.tobeliquidated:
                self.Liquidate(sym)
            self.Debug(f'PRE-LIQUIDATION: {[[sym.Value, self.Portfolio[sym].Quantity] for sym in self.tobeliquidated]}')
            #self.Settings.FreePortfolioValuePercentage = 0.6
            #self.Settings.FreePortfolioValue = self.Settings.FreePortfolioValuePercentage * self.Portfolio.TotalHoldingsValue
            for sym in self.tohold:
                self.SetHoldings(str(sym),1/self.numsecurities)
            self.Debug(f'PRE-SET-QUANTITY: {[[sym.Value,self.Portfolio[sym].Quantity] for sym in self.tohold]}')
            self.Debug(f'EXPECTED CURRENT STATE: {sorted([sym.Value for sym in self.tohold])}')
            if self.debug_execution == False:
                self.debug_execution = True
        elif self.debug_execution == True:
            self.debug_execution = False
            self.Debug(f'========== {(self.Time - self.lasttime).seconds / 3600} HOURS AFTER ORDER IS EXECUTED ==========')
            self.Debug(f'POST-LIQUIDATION: {[[sym.Value, self.Portfolio[sym].Quantity] for sym in self.tobeliquidated]}')
            self.Debug(f'POST-SET-QUANTITY: {[[sym.Value,self.Portfolio[sym].Quantity] for sym in self.tohold]}')
            self.Debug(f'PORTFOLIO CASH AFTER REBALANCING: {self.Portfolio.Cash}')
            self.Debug(f'PORTFOLIO UNSETTLED CASH AFTER REBALANCING: {self.Portfolio.UnsettledCash}')
            self.Debug(f'ACTUAL CURRENT STATE: {sorted([x.Key.Value for x in self.Portfolio if x.Value.Invested])}')
            self.Debug(f'PORTFOLIO TOTAL HOLDINGS VALUE: {self.Portfolio.TotalHoldingsValue}')
            self.Debug(f'PORTFOLIO TOTAL EQUITY VALUE: {self.Portfolio.TotalPortfolioValue}')
            self.Debug(f'PORTFOLIO TOTAL (HOLDINGS - EQUITY) VALUE: {self.Portfolio.TotalHoldingsValue - self.Portfolio.TotalPortfolioValue}')
            
    def OnData(self, data):
        return
    
    def CoarseSelectionFunction(self,coarse):
        if self.IsWarmingUp: return
        if self.lasttime == None or self.Time-self.lasttime >= self.rebalance_interval:
            self.lasttime = self.Time
            self.rebalance = True
            return [x.Symbol for x in coarse if x.HasFundamentalData]
        else:
            return Universe.Unchanged
    
    def FineSelectionFunction(self,fine):
        security_momo_list = []
        MKTCAP_dict = {}
        #exclude delisted and TOPS (due to split value issue)
        excluded_delisted = [i for i in fine if isinstance(i.SecurityReference.DelistingDate.date(),datetime) == False and i.Symbol.Value != "TOPS"]
        
        #filter by mkt_cap
        for i in fine:
            if isinstance(i.MarketCap,(float,int)) and i.MarketCap != 0:
                MKTCAP_dict[i]=i.MarketCap
        microcap = [i for i in excluded_delisted if isinstance(MKTCAP_dict.get(i),(int,float)) and MKTCAP_dict.get(i)>25e6 and MKTCAP_dict.get(i)<250e6]
        
        #filter by Price-to-Sales Ratio < 1 (defined to be null if result <= 0)
        micro_PSR = [i for i in microcap if isinstance(i.ValuationRatios.PSRatio,(float,int)) and i.ValuationRatios.PSRatio < 1 and i.ValuationRatios.PSRatio > 0]
        micro_PSR_symbols = [i.Symbol for i in micro_PSR]
        #sorting by momentum using rolling window
      
        security_momo_list = [[sym, self.history_dict[sym].get_crit_momo()] for sym in self.history_dict.keys() if self.history_dict[sym].eval_self_momo() == True]
        security_momo_list_sorted = sorted(security_momo_list,key = lambda i : i[1],reverse = True)
        self.tohold = [f[0] for f in security_momo_list_sorted[:self.numsecurities]]
        #self.Debug(f'{[f.Value for f in output]}')
        #output = [f[0] for f in security_momo_list]
        return micro_PSR_symbols

    def OnSecuritiesChanged(self, changed):
        for security in changed.AddedSecurities:
            sym = security.Symbol
            if sym not in self.history_dict:
                self.history_dict[sym] = hist_window(self,sym)
        
        for security in changed.RemovedSecurities:
            self.history_dict.pop(sym, None)

class hist_window():
    
    def __init__(self,algorithm,symbol,criterion_period = None):
        self.Symbol = symbol
        #self.Ticker = self.Symbol.Value
        self.periods=sorted(algorithm.periods)
        self.maxperiod = max(self.periods)
        self.Window = RollingWindow[TradeBar](self.maxperiod + 1)
        #periods quantified in days
        #period sorted in increasing length of period
        self.maxperiod = max(self.periods)
        if criterion_period == None:
            self.criterion_period = self.maxperiod
        else:
            self.criterion_period = criterion_period
        #in order of increasing recency, last element being most recent tradebar
        hist_bars = algorithm.History(self.Symbol, self.maxperiod+1, Resolution.Daily)
        for bar in hist_bars.itertuples():
            tradebar = TradeBar(bar.Index[1], self.Symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
            self.Window.Add(tradebar)
        ticker = self.Symbol
        algorithm.Debug(f'TICKER: {ticker}')
        algorithm.Consolidate(ticker, timedelta(days=1), lambda x: self.Window.Add(x))
        
    def get_momo(self, period):
        if self.Window.Count >= period:
            return self.Window[0].Close/self.Window[period].Close/period
        else:
            return None
            
    def get_crit_momo(self):
        return self.get_momo(self.criterion_period)
    
    def eval_self_momo(self):
        if self.get_momo(self.maxperiod) == None:
            return False

        for i in range(0,len(self.periods)-1):
            if self.get_momo(self.periods[i]) < self.get_momo(self.periods[i+1]):
                return False
        return True