Overall Statistics |
Total Trades 844 Average Win 0.98% Average Loss -0.96% Compounding Annual Return 11.071% Drawdown 33.800% Expectancy 0.188 Net Profit 106.165% Sharpe Ratio 0.49 Probabilistic Sharpe Ratio 4.543% Loss Rate 41% Win Rate 59% Profit-Loss Ratio 1.03 Alpha 0.106 Beta -0.086 Annual Standard Deviation 0.196 Annual Variance 0.038 Information Ratio -0.064 Tracking Error 0.252 Treynor Ratio -1.111 Total Fees $3729.60 Estimated Strategy Capacity $110000000.00 Lowest Capacity Asset BTC 1S1 |
from alpha import EmaCrossoverAlphaModel from portfolio import NaiveFuturesPortfolioConstructionModel class EmaCrossoverFutures(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 1, 1) self.SetCash(1_000_000) self.Settings.EnableAutomaticIndicatorWarmUp = True futures = [Futures.Metals.Gold, Futures.Energies.CrudeOilWTI, Futures.Indices.SP500EMini, Futures.Currencies.BTC] for future in futures: self.AddFuture(future, dataNormalizationMode = DataNormalizationMode.BackwardsRatio, dataMappingMode = DataMappingMode.OpenInterest, contractDepthOffset = 0) self.AddAlpha(EmaCrossoverAlphaModel()) self.Settings.FreePortfolioValuePercentage = .1 self.SetPortfolioConstruction(NaiveFuturesPortfolioConstructionModel())
from collections import defaultdict from datetime import datetime, timedelta from itertools import groupby import pytz class NaiveFuturesPortfolioConstructionModel(PortfolioConstructionModel): def __init__(self): self.insightCollection = InsightCollection() self.contracts = {} self.securities = {} self.removedSymbols = [] self.nextExpiryTime = datetime.min.replace(tzinfo=pytz.utc) self.rebalanceFreq = timedelta(1) self.maxGrossExposurePerSecurity = 10 def CreateTargets(self, algorithm, insights): targets = [] if not self.ShouldCreateTargets(algorithm.UtcTime, insights): return targets self.insightCollection.AddRange(insights) targets.extend(self.CreateZeroQuantityTargetsForRemovedSecurities()) targets.extend(self.CreateZeroQuantityTargetsForExpiredInsights(algorithm)) lastActiveInsights = self.GetLastActiveInsights(algorithm) if self.ShouldUpdateTargetPercent(algorithm, lastActiveInsights): targets.extend(self.DeterminePortfolioTargets(algorithm, lastActiveInsights)) self.UpdateNextExpiryTime(algorithm) return targets def DeterminePortfolioTargets(self, algorithm, lastActiveInsights): targets = [] if not lastActiveInsights: return targets TPV = algorithm.Portfolio.TotalPortfolioValue n = max(1, len({insight.Symbol for insight in lastActiveInsights if insight.Direction != InsightDirection.Flat})) for insight in lastActiveInsights: symbol = insight.Symbol if symbol not in self.securities: targets.append(PortfolioTarget(symbol, 0)) continue security = self.securities[symbol] notionalValue = security.Close*security.SymbolProperties.ContractMultiplier weight = min(insight.Direction*notionalValue*self.maxGrossExposurePerSecurity/TPV/n, self.maxGrossExposurePerSecurity) quantity = math.floor(weight*TPV/notionalValue) targets.append(PortfolioTarget(symbol, quantity)) return targets def ShouldCreateTargets(self, time, insights): return len(insights) > 0 or (time > self.nextExpiryTime) def CreateZeroQuantityTargetsForRemovedSecurities(self): if len(self.removedSymbols) == 0: return [] zeroTargets = [PortfolioTarget(symbol, 0) for symbol in self.removedSymbols] self.insightCollection.Clear(self.removedSymbols) self.removedSymbols = [] return zeroTargets def CreateZeroQuantityTargetsForExpiredInsights(self, algorithm): zeroTargets = [] expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime) if len(expiredInsights) == 0: return zeroTargets key = lambda insight: insight.Symbol for symbol, _ in groupby(sorted(expiredInsights, key=key), key): if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime): zeroTargets.append(PortfolioTarget(symbol, 0)) continue return zeroTargets def GetLastActiveInsights(self, algorithm): activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) lastActiveInsights = [] groupedInsights = GroupBy(activeInsights, key = lambda insight: (insight.Symbol, insight.SourceModel)) for kvp in groupedInsights: lastActiveInsights.append(sorted(kvp[1], key=lambda insight: insight.GeneratedTimeUtc)[-1]) return lastActiveInsights def ShouldUpdateTargetPercent(self, algorithm, lastActiveInsights): if algorithm.UtcTime > self.nextExpiryTime: return True for insight in lastActiveInsights: if insight.Direction != InsightDirection.Flat and not algorithm.Portfolio[insight.Symbol].Invested: return True elif insight.Direction != InsightDirection.Up and algorithm.Portfolio[insight.Symbol].IsLong: return True elif insight.Direction != InsightDirection.Down and algorithm.Portfolio[insight.Symbol].IsShort: return True else: continue return False def UpdateNextExpiryTime(self, algorithm): self.nextExpiryTime = self.insightCollection.GetNextExpiryTime() if self.nextExpiryTime is None: self.nextExpiryTime = algorithm.UtcTime + self.rebalanceFreq def OnSecuritiesChanged(self, algorithm, changes): for security in changes.RemovedSecurities: symbol = security.Symbol self.removedSymbols.append(symbol) self.securities.pop(symbol, None) for security in changes.AddedSecurities: self.securities[security.Symbol] = security def GroupBy(iterable, key=lambda x: x): d = defaultdict(list) for item in iterable: d[key(item)].append(item) return d.items()
class EmaCrossoverAlphaModel(AlphaModel): def __init__(self): self.symbolDataDict = {} self.date = None def Update(self, algorithm, data): insights = [] if self.date == algorithm.Time.date(): return insights self.date = algorithm.Time.date() for symbol, symbolData in self.symbolDataDict.items(): if symbolData.Security.HasData and symbol in data.QuoteBars: insight = symbolData.CreateInsight() insights.append(insight) return insights def OnSecuritiesChanged(self, algorithm, changes): for security in changes.RemovedSecurities: self.symbolDataDict.pop(security.Symbol, None) for security in changes.AddedSecurities: if security.Symbol not in self.symbolDataDict: self.symbolDataDict[security.Symbol] = SymbolData(algorithm, security.Symbol) class SymbolData: def __init__(self, algorithm, symbol): self.algorithm = algorithm self.Symbol = symbol self.Security = algorithm.Securities[symbol] barsPerDay = self.Security.Exchange.Hours.RegularMarketDuration.seconds/Extensions.ToTimeSpan(Resolution.Minute).seconds periodFast, periodSlow = [int(barsPerDay*10), int(barsPerDay*50)] self.fast = algorithm.EMA(symbol, periodFast, Resolution.Minute) self.slow = algorithm.EMA(symbol, periodSlow, Resolution.Minute) self.tol = .01 self.futureName = symbol.ID.ToString().split()[0] self.scheduledEvent = algorithm.Schedule.On(algorithm.DateRules.EveryDay(symbol), algorithm.TimeRules.At(12,0), self.UpdateCharts ) def CreateInsight(self): return Insight.Price(self.Symbol, timedelta(2), self.InsightDirection) @property def InsightDirection(self): if not self.slow.IsReady: return InsightDirection.Flat if self.fast.Current.Value > self.slow.Current.Value*(1 + self.tol): return InsightDirection.Up elif self.fast.Current.Value < self.slow.Current.Value*(1 - self.tol): return InsightDirection.Down else: return InsightDirection.Flat def UpdateCharts(self): if (self.Security.HasData and self.slow.IsReady): self.algorithm.Plot('Signals ' + self.futureName, 'Price', self.Security.Close) self.algorithm.Plot('Signals ' + self.futureName, 'FastEMA', self.fast.Current.Value) self.algorithm.Plot('Signals ' + self.futureName, 'SlowEMA', self.slow.Current.Value)