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" --------------------------- ")