Overall Statistics |
Total Trades 181 Average Win 1.45% Average Loss -0.97% Compounding Annual Return 106.800% Drawdown 37.000% Expectancy 0.747 Net Profit 108.039% Sharpe Ratio 2.467 Probabilistic Sharpe Ratio 77.315% Loss Rate 30% Win Rate 70% Profit-Loss Ratio 1.49 Alpha 1.042 Beta -0.204 Annual Standard Deviation 0.395 Annual Variance 0.156 Information Ratio 1.211 Tracking Error 0.532 Treynor Ratio -4.766 Total Fees $831.68 Estimated Strategy Capacity $56.00 |
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(2020, 2, 28) self.SetEndDate(2021, 3, 1) self.SetCash(100000) # Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.lastmonth = -1 self.lastday = -1 self.monthinterval = 3 self.Symbols = None self.tobeliquidated = None self.numsecurities = 25 #self.SetWarmUp(timedelta(365)) def OnData(self, data): if self.IsWarmingUp: return '''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 self.Time.day == self.lastday + 1 and self.Time.month == self.lastmonth: self.Log('========== AFTER ORDER IS EXECUTED ==========') self.Log(f'POST-LIQUIDATION: {[[sym.Value, self.Portfolio[sym].Quantity] for sym in self.tobeliquidated]}') self.Log(f'POST-SET-QUANTITY: {[[sym.Value,self.Portfolio[sym].Quantity] for sym in self.Symbols]}') self.Log(f'PORTFOLIO CASH AFTER REBALANCING: {self.Portfolio.Cash}') self.Log(f'PORTFOLIO UNSETTLED CASH AFTER REBALANCING: {self.Portfolio.UnsettledCash}') self.Log(f'ACTUAL CURRENT STATE: {sorted([x.Key.Value for x in self.Portfolio if x.Value.Invested])}') def CoarseSelectionFunction(self,coarse): if self.IsWarmingUp: return if self.lastmonth == -1 or ((self.Time.month-self.lastmonth) % self.monthinterval == 0 and self.Time.month != self.lastmonth): self.lastmonth = self.Time.month self.lastday = self.Time.day return [x.Symbol for x in coarse if x.HasFundamentalData] else: return Universe.Unchanged def FineSelectionFunction(self,fine): #momo_dict = {} security_momo_list = [] MKTCAP_dict = {} #exclude delisted excluded_delisted = [i for i in fine if isinstance(i.SecurityReference.DelistingDate.date(),datetime) == False] #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] #sorting by momentum hist = self.History([i.Symbol for i in micro_PSR], 365, Resolution.Daily) self.Log(f'INDICES: {hist.index}') #data_top= hist.head() #header_list = [] #for row in data_top.index: # if row[0] not in header_list: # header_list.append(row[0]) #self.Debug(f'LIST OF KEYS: {header_list}') for i in micro_PSR: #hist = self.History(i.Symbol, 365, Resolution.Daily) close_list = hist.loc[str(i.Symbol)]['close'].tolist() if len(close_list) == 365: #self.Debug(f'LENGTH IS: {len(close_list)}') curr_price = close_list[364] price_1Y = close_list[0] price_6M = close_list[180] price_3M = close_list[90] #if i.Symbol.Value == "TOPS": # self.Log(f'CURRENT PRICE: {curr_price}, BASELINE PRICE: {baseline_price}') momo_3M = curr_price/price_3M/90 momo_6M = curr_price/price_6M/180 momo_1Y = curr_price/price_1Y/364 if momo_3M > momo_6M and momo_6M > momo_1Y: security_momo_list.append([i.Symbol,momo_1Y]) security_momo_list_sorted = sorted(security_momo_list,key = lambda i : i[1],reverse = True) output = [f[0] for f in security_momo_list_sorted[:self.numsecurities]] #output = [f[0] for f in security_momo_list] self.Symbols = output return output def OnSecuritiesChanged(self, changes): # selected symbols will be found in Log self.Log('\n'+f'========== NEW CYCLE ==========') #self.Log(f'New Securities Added: {[security.Symbol.Value for security in changes.AddedSecurities]}') #self.Log(f'Securities Removed{[security.Symbol.Value for security in changes.RemovedSecurities]}') self.Log(f'PORTFOLIO CASH BEFORE LIQUIDATION: {self.Portfolio.Cash}') self.Log(f'PORTFOLIO UNSETTLED CASH BEFORE LIQUIDATION: {self.Portfolio.UnsettledCash}') self.tobeliquidated = [security.Symbol for security in changes.RemovedSecurities] for sym in self.tobeliquidated: self.Liquidate(sym) self.Log(f'PRE-LIQUIDATION: {[[sym.Value, self.Portfolio[sym].Quantity] for sym in self.tobeliquidated]}') for sym in self.Symbols: self.SetHoldings(str(sym),1/self.numsecurities) self.Log(f'PRE-SET-QUANTITY: {[[sym.Value,self.Portfolio[sym].Quantity] for sym in self.Symbols]}') self.Log(f'EXPECTED CURRENT STATE: {sorted([sym.Value for sym in self.Symbols])}') return