| Overall Statistics |
|
Total Trades 32 Average Win 0.52% Average Loss -0.90% Compounding Annual Return 64.658% Drawdown 7.700% Expectancy 0.022 Net Profit 7.070% Sharpe Ratio 2.082 Probabilistic Sharpe Ratio 61.464% Loss Rate 35% Win Rate 65% Profit-Loss Ratio 0.58 Alpha 0.649 Beta -0.148 Annual Standard Deviation 0.288 Annual Variance 0.083 Information Ratio 0.845 Tracking Error 0.308 Treynor Ratio -4.041 Total Fees $32.00 |
import pandas as pd
class ParticleNadirsonInterceptor(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 12, 2) # Set Start Date
self.SetEndDate(2021, 1, 20) # Set End Date
self.SetCash(10000) # Set Strategy Cash
self.current_month = -1
self.coarse_count = 10
self.fine_count = 5
self.benchmark = "SPY"
self.resolution = Resolution.Daily
self.SetWarmUp(timedelta(minutes = 10))
self.UniverseSettings.Resolution = self.resolution
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionRsBased)
self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(30)))
myPCM = EqualWeightingPortfolioConstructionModel()
myPCM.RebalanceOnInsightChanges = False
myPCM.RebalanceOnSecurityChanges = True
self.SetPortfolioConstruction(myPCM)
self.SetExecution(ImmediateExecutionModel())
def CoarseSelectionFunction(self, coarse):
if self.current_month == self.Time.month:
return Universe.Unchanged
self.current_month = self.Time.month
sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData],
key=lambda x: x.DollarVolume, reverse=True)[:self.coarse_count]
self.Debug(f"No of Securities in Coarse Selection: {len([i.Symbol for i in sortedByDollarVolume])}")
return [i.Symbol for i in sortedByDollarVolume]
def FineSelectionRsBased(self, fine):
resultSet={}
rsBenchmark=self.getRslFactor(self.benchmark)
for x in fine:
resultSet[x.Symbol]=self.getRslFactor(str(x.Symbol.Value))
resultSet=sorted([x for x in resultSet.items() if x[1]>rsBenchmark], key=lambda x: x[1], reverse=True)[:self.fine_count]
self.Debug(f"No of Securities in Fine Selection: {len([x[0] for x in resultSet])}")
for security in self.ActiveSecurities.Keys:
self.Log("Security: " + str(security.Value))
self.RemoveSecurity(security.Value)
return [x[0] for x in resultSet]
def getRslFactor(self,symbol):
self.AddEquity(symbol, Resolution.Daily)
# lookback days : weight
days = {40:0.6,80:0.25,160:0.15}
result=[]
df=pd.DataFrame(self.History(self.Symbol(symbol), 300, Resolution.Daily))
df=df.iloc[::-1]
df=df.reset_index(level=0, drop=True)
for x in days:
if len(df)>int(x):
result.append([symbol, x, df.iloc[0]['close'], df.iloc[x-1]['close'],days[x]])
else:
return -1000 # missing data workaround
df = pd.DataFrame(result,columns=['Symbol','Days','Ref_Price','Close_Price','Weight'],dtype=float)
df = df.assign(Rsl=(df['Ref_Price'])/df['Close_Price']*df['Weight'])
rsl= round(float((abs(df['Rsl']).sum()*1000)-1000),5)
return rsl# selection - RS based / Z-score?
# risk - manual (hourly) or mean reverse
# rebalance - weekly based on growth
# cash - increase size monthly
# Returns True if TradeBar data is present else False
#if not data.Bars.ContainKey(symbol):
#return
#self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFundamental)
# def FineSelectionFundamental(self, fine):
# fine = [x for x in fine if x.EarningReports.TotalDividendPerShare.ThreeMonths
# and x.ValuationRatios.PriceChange1M
# and x.ValuationRatios.BookValuePerShare
# and x.ValuationRatios.FCFYield]
# sortedByfactor1 = sorted(fine, key=lambda x: x.EarningReports.TotalDividendPerShare.ThreeMonths, reverse=True)
# sortedByfactor2 = sorted(fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=False)
# sortedByfactor3 = sorted(fine, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True)
# sortedByfactor4 = sorted(fine, key=lambda x: x.ValuationRatios.FCFYield, reverse=True)
# stock_dict = {}
# for rank1, ele in enumerate(sortedByfactor1):
# rank2 = sortedByfactor2.index(ele)
# rank3 = sortedByfactor3.index(ele)
# rank4 = sortedByfactor4.index(ele)
# stock_dict[ele] = rank1 + rank2 + rank3 + rank4
# sorted_stock = sorted(stock_dict.items(),
# key=lambda d:d[1], reverse=True)[:self.fine_count]
# return [x[0].Symbol for x in sorted_stock]