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