Overall Statistics |
Total Trades 1133 Average Win 1.28% Average Loss -0.46% Compounding Annual Return 77.302% Drawdown 19.000% Expectancy 1.158 Net Profit 1800.905% Sharpe Ratio 2.288 Probabilistic Sharpe Ratio 99.431% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 2.78 Alpha 0.505 Beta 0.135 Annual Standard Deviation 0.229 Annual Variance 0.052 Information Ratio 1.454 Tracking Error 0.264 Treynor Ratio 3.888 Total Fees $1600645.67 Estimated Strategy Capacity $660000.00 Lowest Capacity Asset DOTUSD E3 |
from AlgorithmImports import * class NewImmediateExecutionModel(ExecutionModel): '''Provides an implementation of IExecutionModel that immediately submits market orders to achieve the desired portfolio targets''' def __init__(self): '''Initializes a new instance of the ImmediateExecutionModel class''' self.targetsCollection = PortfolioTargetCollection() self.one = 1 self.zero = self.one - self.one def Execute(self, algorithm, targets): '''Immediately submits orders for the specified portfolio targets. Args: algorithm: The algorithm instance targets: The portfolio targets to be ordered''' self.targetsCollection.AddRange(targets) if self.targetsCollection.Count > self.zero: for target in self.targetsCollection.OrderByMarginImpact(algorithm): security = algorithm.Securities[target.Symbol] # calculate remaining quantity to be ordered quantity = OrderSizing.GetUnorderedQuantity(algorithm, target, security) if quantity != self.zero: aboveMinimumPortfolio = BuyingPowerModelExtensions.AboveMinimumOrderMarginPortfolioPercentage(security.BuyingPowerModel, security, quantity, algorithm.Portfolio, algorithm.Settings.MinimumOrderMarginPortfolioPercentage) if aboveMinimumPortfolio: if quantity < self.zero and quantity < -algorithm.Portfolio[target.Symbol].Quantity: continue if abs(quantity) < 0.0000001: continue algorithm.MarketOrder(security, quantity) self.targetsCollection.ClearFulfilled(algorithm)
from clr import AddReference AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Algorithm.Framework") from System import * from QuantConnect import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Alphas import * import statistics class LongOnlyAlphaModel(AlphaModel): def __init__(self, period, recalculationDayNumber, refusedSymbols, maxExposure): # 0 / 1 CANCEL INCORRECT PARAMETERS CALCULATIONS self.one = 1 self.zero = self.one - self.one self.two = self.one + self.one # Initializations self.securities = [] self.period = period self.recalculationDayNumber = recalculationDayNumber self.refusedSymbols = refusedSymbols self.maxExposure = maxExposure self.isNewDay = False self.lastDay = self.zero self.counterDay = self.two * self.two def Update(self, algorithm, data): # New Day analysis if self.lastDay == algorithm.Time.day: self.isNewDay = False else: if algorithm.Time.hour == self.zero and algorithm.Time.minute == self.zero: self.isNewDay = True self.lastDay = algorithm.Time.day self.counterDay += self.one insights = [] # List to store the new insights symbol_data = {} # Append insights at (re)launch or between Monday and Tuesday at midnight if (algorithm.Time.weekday() == self.recalculationDayNumber or self.counterDay > 6) and self.isNewDay == True: sum_inverse_stdev = self.zero unavailableSymbols = [] longSymbols = [] allSymbols = [x.Symbol for x in self.securities if x.Symbol not in self.refusedSymbols] weight = self.zero magnitude = self.one confidence = self.one for symbol in allSymbols: symbol_history = algorithm.History(symbol, self.period, Resolution.Daily) if len(symbol_history) < self.two: # Check for usable dataframe unavailableSymbols.append(symbol) continue else: symbol_historical_values = symbol_history["close"][-self.period:] symbol_roc = symbol_historical_values[-self.one] / symbol_historical_values[self.zero] symbol_daily_returns = symbol_historical_values.pct_change()[symbol_historical_values.shift(self.one).notnull()].dropna() if symbol not in unavailableSymbols and not symbol_daily_returns.empty: symbol_stdev = statistics.pstdev(symbol_daily_returns) symbol_data[symbol] = (symbol_roc, symbol_stdev) if symbol_stdev != self.zero and symbol_roc >= self.one: sum_inverse_stdev = sum_inverse_stdev + self.one / symbol_stdev for symbol in allSymbols: if symbol not in unavailableSymbols: longSymbols.append(symbol) if (algorithm.Time.month % self.two) == self.zero and algorithm.Time.month < 6: # "Sell in May and go away" effect seasonweight = self.one else: seasonweight = self.one / 4 sumweight = self.zero if len(longSymbols) != self.zero: for symbol in longSymbols: if symbol_data[symbol][self.zero] >= self.one and symbol_data[symbol][self.one] > self.zero and sum_inverse_stdev > self.zero: weight = ((seasonweight * self.maxExposure) / symbol_data[symbol][self.one]) / (sum_inverse_stdev) sumweight += weight insights.append(Insight.Price(str(symbol), timedelta(days = self.period, minutes = -self.one), InsightDirection.Up, magnitude, confidence, "Long", weight)) if sumweight < self.maxExposure * 0.8: allRefusedSymbols = [x.Symbol for x in self.securities if x.Symbol in self.refusedSymbols] for symbol in allRefusedSymbols: if symbol == 'SPY': continue else: symbol_hist = algorithm.History(symbol, self.period, Resolution.Daily) if len(symbol_hist) > self.two: # Check for usable dataframe symbol_hist_values = symbol_hist["close"][-self.period:] symbol_roc = symbol_hist_values[-self.one] / symbol_hist_values[self.zero] if symbol_roc < self.one: insights.append(Insight.Price(symbol, timedelta(days = self.period, minutes = -self.one), InsightDirection.Up, magnitude, confidence, None, self.maxExposure * 0.8 - sumweight)) self.counterDay = algorithm.Time.weekday() - self.recalculationDayNumber return insights def OnSecuritiesChanged(self, algorithm, changes): # Add new securities for added in changes.AddedSecurities: self.securities.append(added) # Remove old securities for removed in changes.RemovedSecurities: if removed in self.securities: self.securities.remove(removed)
import datetime from LongOnlyAlphaModel import LongOnlyAlphaModel from NewImmediateExecutionModel import NewImmediateExecutionModel from FixedInsightWeightingPortfolioConstructionModel import FixedInsightWeightingPortfolioConstructionModel from QuantConnect.Data.UniverseSelection import * class GEAlgo_Crypto_Momentum(QCAlgorithm): def Initialize(self): # Automatic rebalancing parameters self.Settings.RebalancePortfolioOnInsightChanges = True self.Settings.RebalancePortfolioOnSecurityChanges = False ############################################################################################################################### self.SetStartDate(2016, 11, 13) # 5 years up to the submission date self.SetCash(1000000) # Set $1m Strategy Cash to trade significant AUM self.reference = "SPY" # SPY Benchmark self.AddEquity(self.reference, Resolution.Minute) self.SetBenchmark(self.reference) self.SetBrokerageModel(AlphaStreamsBrokerageModel()) self.SetExecution(NewImmediateExecutionModel()) self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel(self.RebalanceFunction)) ############################################################################################################################### # Universe symbols = ['BTCUSD', 'ETHUSD', 'XRPUSD', 'ADAUSD', 'SOLUSD', 'DOTUSD', 'LTCUSD', 'LUNAUSD', 'USDTUSD'] for symbol in symbols: self.AddCrypto(symbol, Resolution.Minute, Market.Bitfinex) self.SetUniverseSelection(ManualUniverseSelectionModel(symbols)) # INPUTS # Parameters maxExposure = 0.7 # max exposure before discrepancies recalculationDayNumber = 1 # rebalancing between monday and tuesday (more liquidity than during the week-end) period = 7 # rebalancing each week # Symbol "manually" refused: the benchmark and USDTUSD refusedSymbols = [self.reference, 'USDTUSD'] # Alpha model self.AddAlpha(LongOnlyAlphaModel(period = period, recalculationDayNumber = recalculationDayNumber, refusedSymbols = refusedSymbols, maxExposure = maxExposure)) # Risk Management self.AddRiskManagement(NullRiskManagementModel()) def RebalanceFunction(self, time): # Using the manual rebalancing to avoid any rebalancing not needed: the weightings are managed in the Alpha Model return None
from clr import AddReference AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Algorithm.Framework") from System import * from QuantConnect import * from itertools import groupby ############################################################################################################################### # PORTFOLIO CONSTRUCTION MODEL CLASS class FixedInsightWeightingPortfolioConstructionModel(PortfolioConstructionModel): '''Provides an implementation of IPortfolioConstructionModel that gives weighting to all selected securities that are given by the algorithm, without any rebalancing or orders adjustments. Hence the orders sent will not be different from the insights generated, as there will not be any rebalancing for open insights (and open positions) Therefore useless trading costs will be avoided, and the statistics of orders (average win/loss...) will be correct and usable.''' def __init__(self): #, rebalancingParam = Resolution.Daily self.insightCollection = InsightCollection() self.removedSymbols = [] self.one = 1 self.zero = self.one - self.one def ShouldCreateTargetForInsight(self, insight): '''Method that will determine if the portfolio construction model should create a target for this insight Args: insight: The insight to create a target for''' # Ignore insights that don't have Weight value return insight.Weight is not None def DetermineTargetPercent(self, activeInsights): '''Will determine the target percent for each insight Args: activeInsights: The active insights to generate a target for''' result = {} for insight in activeInsights: result[insight] = insight.Direction * self.GetValue(insight) return result def GetValue(self, insight): '''Method that will determine which member will be used to compute the weights and gets its value Args: insight: The insight to create a target for Returns: The value of the selected insight member''' return insight.Weight def CreateTargets(self, algorithm, insights): '''Create portfolio targets from the specified insights Args: algorithm: The algorithm instance insights: The insights to create portfolio targets from Returns: An enumerable of portfolio targets to be sent to the execution model''' targets = [] if (len(insights) == self.zero and self.removedSymbols is None): return targets for insight in insights: if self.ShouldCreateTargetForInsight(insight): self.insightCollection.Add(insight) # Create flatten target for each security that was removed from the universe if self.removedSymbols is not None: universeDeselectionTargets = [PortfolioTarget(symbol, self.zero) for symbol in self.removedSymbols] targets.extend(universeDeselectionTargets) self.removedSymbols = None # Get insight that haven't expired of each symbol that is still in the universe activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) # Get the last generated active insight for each symbol lastActiveInsights = [] for symbol, g in groupby(activeInsights, lambda x: x.Symbol): lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-self.one]) # Determine target percent for the given insights percents = self.DetermineTargetPercent(lastActiveInsights) # Send ONLY NEW insight(s) as orders, NOT all last active insights sent again (= no periodic rebalancing) errorSymbols = {} for insight in insights: target = PortfolioTarget.Percent(algorithm, insight.Symbol, percents[insight]) if not target is None: targets.append(target) else: errorSymbols[insight.Symbol] = insight.Symbol # Get expired insights and create flatten targets for each symbol expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime) expiredTargets = [] for symbol, f in groupby(expiredInsights, lambda x: x.Symbol): if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime) and not symbol in errorSymbols: expiredTargets.append(PortfolioTarget(symbol, self.zero)) continue targets.extend(expiredTargets) return targets