| Overall Statistics |
|
Total Trades 10001 Average Win 0.02% Average Loss -0.04% Compounding Annual Return -100.0% Drawdown 82.400% Expectancy -0.863 Net Profit -82.413% Sharpe Ratio -1.025 Probabilistic Sharpe Ratio 0.000% Loss Rate 91% Win Rate 9% Profit-Loss Ratio 0.46 Alpha 0.594 Beta -0.784 Annual Standard Deviation 0.975 Annual Variance 0.951 Information Ratio -2.968 Tracking Error 1.022 Treynor Ratio 1.276 Total Fees $10157.75 Estimated Strategy Capacity $79000000.00 Lowest Capacity Asset BBBY R735QTJ8XC9X |
from datetime import datetime, timedelta
import numpy as np
from nltk.util import ngrams
class SentimentAlphaModel(AlphaModel):
def __init__(self, algo):
self.algo = algo
self.CandleSize = timedelta(minutes = 5)
self.MaxHoldTime = 20*self.CandleSize
self.Symbols = set()
self.Prices = {}
self.ExitPrices = {}
self.BuyTimes = {}
self.Holdings = set()
self.MaxHoldings = 5
self.CandlesInTraining = {}
self.Candles = {}
self.LastCandleTime = self.algo.Time
self.TradingTime = False
return
def UpdateCandlesInTraining(self, data):
for symbol in self.Symbols:
if symbol in data and data[symbol] is not None:
o, h, l, c = data[symbol].Open, data[symbol].High, data[symbol].Low, data[symbol].Close
candle = self.CandlesInTraining[symbol]
if candle["Open"] is None:
candle["Open"] = o
if candle["High"] is None or h > candle["High"]:
candle["High"] = h
if candle["Low"] is None or l < candle["Low"]:
candle["Low"] = l
candle["Close"] = c
def AddCandles(self):
#Candles in training are finished; HA time!
for symbol in self.Symbols:
o, h, c, l = self.CandlesInTraining[symbol]["Open"], self.CandlesInTraining[symbol]["High"], self.CandlesInTraining[symbol]["Low"], self.CandlesInTraining[symbol]["Close"]
if o is None or h is None or c is None or l is None:
continue
self.Prices[symbol] = c
newCandle = {"Open": o, "High": h, "Low": l, "Close": c}
newCandle["High"] = max([newCandle["Open"], h, newCandle["Close"]])
newCandle["Low"] = min([newCandle["Open"], l, newCandle["Close"]])
if o > c:
newCandle["Color"] = "Red"
else:
newCandle["Color"] = "Green"
newCandle["Top"] = max([newCandle["Open"], newCandle["Close"]])
newCandle["Bottom"] = min([newCandle["Open"], newCandle["Close"]])
self.Candles[symbol].append(newCandle)
def UpdateCandles(self, data):
self.UpdateCandlesInTraining(data)
if self.algo.Time >= self.LastCandleTime + self.CandleSize:
self.AddCandles()
for symbol in self.Symbols:
self.CandlesInTraining[symbol] = self.FreshCandle()
self.LastCandleTime = self.algo.Time
self.TradingTime = True
def FreshCandle(self):
return {"Open": None, "High": None, "Low": None, "Close": None}
def BuySignal(self, symbol):
if symbol not in self.Candles or len(self.Candles[symbol]) < 1:
return 0
return self.Candles[symbol][-1]["High"] - self.Candles[symbol][-1]["Low"]
def SellSignal(self, symbol):
return (symbol not in self.ExitPrices) or (self.Prices[symbol] < self.ExitPrices[symbol][0] or self.Prices[symbol] > self.ExitPrices[symbol][1])
def OnSecuritiesChanged(self, algo, changes):
for symbol in changes.AddedSecurities:
self.Symbols.add(symbol.Symbol)
self.CandlesInTraining[symbol.Symbol] = self.FreshCandle()
self.Candles[symbol.Symbol] = []
for symbol in changes.RemovedSecurities:
self.Symbols.discard(symbol.Symbol)
self.CandlesInTraining.pop(symbol.Symbol, None)
self.Candles.pop(symbol.Symbol, None)
self.ExitPrices.pop(symbol.Symbol, None)
self.Holdings.discard(symbol.Symbol)
self.algo.Liquidate(symbol.Symbol)
def Update(self, algo, data):
self.UpdateCandles(data)
res = []
if not self.TradingTime:
for symbol in self.Symbols:
if symbol in data and data[symbol] is not None and (symbol not in self.ExitPrices or data[symbol].Close < self.ExitPrices[symbol][0] or data[symbol].Close > self.ExitPrices[symbol][1]):
# self.algo.Log(f"Selling {symbol}")
res.append(Insight.Price(symbol, self.MaxHoldTime, InsightDirection.Flat))
self.Holdings.discard(symbol)
return res
buySignals = {}
for symbol in self.Symbols:
buySignals[symbol] = self.BuySignal(symbol)
thingsWeShallBuy = set(sorted([symbol for symbol in buySignals.keys()], key = lambda x: buySignals[x], reverse = True)[:self.MaxHoldings - len(self.Holdings)])
for symbol in self.Symbols:
if self.SellSignal(symbol) and (symbol not in thingsWeShallBuy):
# self.algo.Log(f"Selling {symbol}")
res.append(Insight.Price(symbol, self.MaxHoldTime, InsightDirection.Flat))
self.Holdings.discard(symbol)
for symbol in thingsWeShallBuy:
# self.algo.Log(f"Buying {symbol}")
res.append(Insight.Price(symbol, self.MaxHoldTime, InsightDirection.Up))
self.Holdings.add(symbol)
p = self.Prices[symbol]
bottom = self.Candles[symbol][-1]["Bottom"]
self.ExitPrices[symbol] = (p - 0.5*(p - bottom), p + 2*(p - bottom))
assert(len(self.Holdings) <= self.MaxHoldings)
self.TradingTime = False
return res
class CreativeVioletMule(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 1, 1)
self.SetEndDate(2020, 1, 1)
self.SetCash(100000)
self.MinEarningTime = timedelta(days = 1)
self.MaxEarningTime = timedelta(days = 30)
self.Symbols = set()
self.SecuritiesSet = set()
self.Holdings = set()
self.Coarse_Count = 500
self.Current_Day = None
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.UniverseSettings.Resolution = Resolution.Minute
self.SetAlpha(SentimentAlphaModel(self))
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())
def CoarseSelectionFunction(self, coarse):
if self.Current_Day == self.Time.day:
return Universe.Unchanged
sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData],
key=lambda x: x.DollarVolume, reverse=True)[:self.Coarse_Count]
return [i.Symbol for i in sortedByDollarVolume]
def FineSelectionFunction(self, fine):
if self.Current_Day == self.Time.day:
return Universe.Unchanged
self.Current_Day = self.Time.day
res = [x for x in fine if (x.EarningReports.FileDate > self.Time - self.MaxEarningTime and x.EarningReports.FileDate < self.Time - self.MinEarningTime)]
symbolRes = [x.Symbol for x in res]
return symbolRes
def OnData(self, data):
return