Overall Statistics |
Total Orders 4708 Average Win 0.66% Average Loss -0.49% Compounding Annual Return 68.742% Drawdown 56.100% Expectancy 0.303 Start Equity 1000000 End Equity 22433010.19 Net Profit 2143.301% Sharpe Ratio 1.306 Sortino Ratio 1.395 Probabilistic Sharpe Ratio 60.440% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.33 Alpha 0.364 Beta 1.555 Annual Standard Deviation 0.408 Annual Variance 0.166 Information Ratio 1.285 Tracking Error 0.33 Treynor Ratio 0.343 Total Fees $444814.51 Estimated Strategy Capacity $950000.00 Lowest Capacity Asset KXI TM694I8OJJAD Portfolio Turnover 22.19% |
from AlgorithmImports import * import random class RandomMonthlyStockPurchases(QCAlgorithm): def Initialize(self): self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseFilter, self.FineFilter) self.SetStartDate(2019, 1, 1) self.SetCash(1000000) self.collector = 0 self.minPrice = 10 self.maxPrice = 500 self.courseNumStocks = 1000 self.fineNumStocks = 500 self.TradeCount = 2 self.historyTimeFrame = 3 self.holdPeriod = 30 self.holdermultiple = (self.holdPeriod/self.historyTimeFrame)/2 self.trade_symbols = {} self.tradesByPriceDateSharesSymbol = {} self.tradesByPriceDateSharesStockToRemove = [] self.isLongStrategy = True self.valueOfportfolio = [] self.spy = self.AddEquity("SPY", Resolution.DAILY).symbol self.momp2 = self.momp(self.spy, 2).current.value self.momp5 = self.momp(self.spy, 5).current.value self.momp10 = self.momp(self.spy, 10).current.value self.AddEquity("SPXU", Resolution.Daily) self.AddEquity("KXI", Resolution.Daily) self.AddEquity("DIG", Resolution.Daily) self.AddEquity("DBC", Resolution.Daily) self.AddEquity("UPRO", Resolution.Daily) def CoarseFilter(self, coarse): selected_by_dollar_volume = sorted([x for x in coarse if x.price > self.minPrice and x.price < self.maxPrice and x.has_fundamental_data and (((self.time - x.SecurityReference.IPODate).days) >= 180*5 if self.holdPeriod < 180 else 5*self.holdPeriod)], key = lambda x: x.dollar_volume, reverse = self.isLongStrategy) return [x.symbol for x in selected_by_dollar_volume[:self.courseNumStocks]] def FineFilter(self, fine): sortedByMarketCap = sorted(fine, key=lambda x: x.MarketCap, reverse = self.isLongStrategy)[:self.fineNumStocks] symbols = [x.Symbol for x in sortedByMarketCap] history = self.History(symbols, self.historyTimeFrame+1, Resolution.Daily).close.unstack(0) averages = dict() for symbol in symbols: if symbol in history: df = history[symbol].dropna() if df.empty: continue count = self.historyTimeFrame-1 if len(df.index) >= count: try: averages[symbol] = df[0] - df[count] except: self.debug(f"count: {count} df len {len(df.index)}") sortedbyMomentum = sorted(averages.items(), key=lambda x: x[1], reverse=True) self.trade_symbols = [x[0] for x in sortedbyMomentum[:self.TradeCount]] return self.trade_symbols def OnData(self, data): accumulatedValue = 0 for dateOfTrade, tradeOrder in self.tradesByPriceDateSharesSymbol.items(): for stock, openPriceAndShares in tradeOrder.items(): accumulatedValue = accumulatedValue + self.Securities[stock].Price * openPriceAndShares[1] for PriceDateSharesSymbol in self.tradesByPriceDateSharesStockToRemove: accumulatedValue = accumulatedValue + self.Securities[PriceDateSharesSymbol[3]].Price * PriceDateSharesSymbol[0][1] self.valueOfportfolio.append(accumulatedValue) length = len( self.valueOfportfolio) isLongEnough30 = length > 40 + 5 isLongEnough20 = length > 30 + 5 self.valueOfportfolio.append(accumulatedValue) is2Day = 1 if 0 < self.momp2 else 0 is5Day = 1 if 0 < self.momp5 else 0 is10Day = 1 if 0 < self.momp10 else 0 momentumScore = is2Day + is5Day + is10Day uproAmount = .5 spxuAmount = .2 if isLongEnough30 and .87 > self.valueOfportfolio[length-1]/self.valueOfportfolio[length-30]: # self.SetHoldings("SPXU", 0.2) self.SetHoldings("KXI", 0.1) self.SetHoldings("DBC", 0.1) self.SetHoldings("DIG", 0.1) # self.SetHoldings("UPRO", 0) self.debug("BEAR----------") uproAmount = uproAmount - .5 spxuAmount = spxuAmount +.2 elif isLongEnough20 and .95 > self.valueOfportfolio[length-1]/self.valueOfportfolio[length-20]: # self.SetHoldings("SPXU", 0.1) self.SetHoldings("KXI", 0.1) self.SetHoldings("DBC", 0.1) self.SetHoldings("DIG", 0.1) #self.SetHoldings("UPRO", 0.1) self.debug("DOVE----------") uproAmount = uproAmount+ .1 spxuAmount = spxuAmount +.1 else: # self.SetHoldings("SPXU", 0) self.SetHoldings("KXI", 0) self.SetHoldings("DBC", 0) self.SetHoldings("DIG", 0) # self.SetHoldings("UPRO", 0.50) self.debug("BULL----------") uproAmount = uproAmount + .5 spxuAmount = spxuAmount- .2 if momentumScore > 2: self.SetHoldings("SPXU", spxuAmount) self.SetHoldings("UPRO", uproAmount) else: self.SetHoldings("SPXU", spxuAmount) self.SetHoldings("UPRO", uproAmount) self.debug(f"Value: {self.portfolio.total_portfolio_value}\ Margin Remaining: {self.portfolio.margin_remaining}\ Cash: {self.portfolio.cash}") liquidate = [] for PriceDateSharesSymbol in self.tradesByPriceDateSharesStockToRemove: proRatedProfitMargin = 1+(1.12*(round((self.Time - PriceDateSharesSymbol[1]).days/365,3))*.02) proRatedProfitMargin = 1.02 if proRatedProfitMargin < 1.02 else proRatedProfitMargin if (self.isLongStrategy and PriceDateSharesSymbol[0][0] > self.Securities[PriceDateSharesSymbol[3]].Price * proRatedProfitMargin) or\ (not self.isLongStrategy and PriceDateSharesSymbol[0][0] < self.Securities[PriceDateSharesSymbol[3]].Price * proRatedProfitMargin): self.debug(f"LIQUIDATE from remove: {PriceDateSharesSymbol[3]}") self.order(PriceDateSharesSymbol[3], -PriceDateSharesSymbol[0][1]) liquidate.append(PriceDateSharesSymbol) else: self.debug(f"KEEP IN REMOVE: {PriceDateSharesSymbol[3]}") for PriceDateSharesSymbol in liquidate: if PriceDateSharesSymbol in self.tradesByPriceDateSharesStockToRemove: self.tradesByPriceDateSharesStockToRemove.remove(PriceDateSharesSymbol) positionWentOverSomehow = [x.Key for x in self.Portfolio if x.Value.invested and (x.Value.is_short if self.isLongStrategy else x.Value.is_long)] for stock in positionWentOverSomehow: self.debug(f"LIQUIDATE wrong direction: {stock}") self.liquidate(stock) if not self.trade_symbols: return remove = "" for dateOfTrade, tradeOrder in self.tradesByPriceDateSharesSymbol.items(): if (self.Time - dateOfTrade).days >= self.holdPeriod: for stock, openPriceAndShares in tradeOrder.items(): remove = dateOfTrade shares = openPriceAndShares[1] price = self.Securities[stock].Price if (self.isLongStrategy and openPriceAndShares[0] < price) or\ (not self.isLongStrategy and openPriceAndShares[0] > price): self.order(stock, -shares) self.debug(f"CLOSE {self.time} order: {stock}, shares: {shares}") else: self.tradesByPriceDateSharesStockToRemove.append([openPriceAndShares,dateOfTrade, shares, stock]) self.debug(f"RESET {self.time} holding: {stock}") if remove in self.tradesByPriceDateSharesSymbol: del self.tradesByPriceDateSharesSymbol[remove] holder = {} trade = False if self.collector < self.historyTimeFrame: self.collector = 1 + self.collector else: portfolioValue = self.portfolio.total_portfolio_value for symbol in self.trade_symbols: price = self.Securities[symbol].price if self.Securities[symbol] and price > 0: multiple = 1 for dateOfTrade, tradeOrder in self.tradesByPriceDateSharesSymbol.items(): if (self.Time - dateOfTrade).days >= self.holdPeriod: for alreadyHolding, openPriceAndShares in tradeOrder.items(): if symbol == alreadyHolding: multiple = multiple+1 quantity = self.historyTimeFrame/self.holdPeriod/self.TradeCount shares = self.CalculateOrderQuantity(symbol,quantity*multiple) shares = shares if self.isLongStrategy else -shares self.order(symbol, shares) holder[symbol] = [price,shares] if self.isLongStrategy else [price,shares] self.debug(f"OPEN {self.time} order: {symbol}, shares: {shares}") self.tradesByPriceDateSharesSymbol[self.Time] = holder self.collector = 0 self.debug(f" --- Portfolio positions --- ") for var in [x for x in self.Portfolio if x.Value.invested]: self.debug(f"{var}") self.debug(f" --------------------------- ")