Overall Statistics |
Total Trades 6637 Average Win 0.06% Average Loss -0.06% Compounding Annual Return -40.963% Drawdown 54.900% Expectancy -0.344 Net Profit -44.440% Sharpe Ratio -0.458 Probabilistic Sharpe Ratio 2.826% Loss Rate 66% Win Rate 34% Profit-Loss Ratio 0.93 Alpha -0.169 Beta 1.846 Annual Standard Deviation 0.472 Annual Variance 0.223 Information Ratio -0.542 Tracking Error 0.353 Treynor Ratio -0.117 Total Fees $6920.57 Estimated Strategy Capacity $7600000.00 Lowest Capacity Asset FEAC XE0YKW62KXGL |
import numpy as np from scipy import stats from collections import deque #region imports from AlgorithmImports import * #endregion from Execution.ImmediateExecutionModel import ImmediateExecutionModel from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel class NadionTransdimensionalAutosequencers(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 10, 1) # Set Start Date self.SetCash(100000) # Set Strategy Cash self.SetBenchmark("SPY") self.UniverseSettings.Leverage = 0 self.Settings.RebalancePortfolioOnInsightChanges = True self.Settings.RebalancePortfolioOnSecurityChanges = True self.SetExecution(ImmediateExecutionModel()) self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) self.UniverseSettings.Resolution=Resolution.Daily self.SetUniverseSelection( QC500UniverseSelectionModel() ) self.SetAlpha(MyAlpha()) class MyAlpha(AlphaModel): def __init__(self): self.securities = [] self.symbol_data_by_symbol = {} self.sorted_mom = [] self.mom_scores = {} self.num_stocks = 30 def Update(self, algorithm, data): insight = [] for k,v in self.symbol_data_by_symbol.items(): #algorithm.Debug(str(repr(v.mom))) if not v.mom.IsReady: return if v.mom.Score >= 40: self.mom_scores[k] = v.mom.Score # algorithm.Debug(f"{k}: {v.mom.Score}") self.sorted_mom = sorted([k for k,v in self.mom_scores.items()], key=lambda x: self.mom_scores[x], reverse=True) self.selected = self.sorted_mom[:self.num_stocks] for security in self.selected: insight += [(Insight.Price(security, timedelta(days = 1), InsightDirection.Up))] #algorithm.Debug(f"{algorithm.Time}:update insights") return insight def OnSecuritiesChanged(self, algorithm, changes): #algorithm.Debug(f"{algorithm.Time}:security changes called") #algorithm.Debug(f"removed: {len(changes.RemovedSecurities)}") #algorithm.Debug(f"added: {len(changes.AddedSecurities)}") for security in changes.AddedSecurities: self.symbol_data_by_symbol[security.Symbol] = SymbolData(algorithm, security.Symbol) #algorithm.Debug(f"{algorithm.Time}: Added {security.Symbol}") for security in changes.RemovedSecurities: if security.Symbol in self.symbol_data_by_symbol: symbol_data = self.symbol_data_by_symbol.pop(security.Symbol, None) if symbol_data: symbol_data.dispose() #algorithm.Debug(f"{algorithm.Time}: Removed {security.Symbol}") if security in self.securities: self.securities.remove(security) self.securities.extend(changes.AddedSecurities) for security in self.securities: if security.Symbol not in algorithm.ActiveSecurities: algorithm.Debug(f"{security} not in active but is in self") self.securities.remove(security) symbol_data = self.symbol_data_by_symbol.pop(security.Symbol, None) if symbol_data: symbol_data.dispose() algorithm.Debug(f"{security.Symbol} data removed") class SymbolData: def __init__(self, algorithm, symbol): self.algorithm = algorithm self.symbol = symbol self.mom = CustomMomentum("momentum", 125) self.consolidator = TradeBarConsolidator(1) algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator) algorithm.RegisterIndicator(symbol, self.mom, self.consolidator) algorithm.WarmUpIndicator(self.symbol, self.mom) def dispose(self): self.algorithm.SubscriptionManager.RemoveConsolidator(self.symbol, self.consolidator) class CustomMomentum(PythonIndicator): def __init__(self, name, period): self.Name = name self.WarmUpPeriod = period self.Time = datetime.min self.Value = 0 self.queue = deque(maxlen=period) def __repr__(self): return "{0} -> IsReady: {1}. Time: {2}. Value: {3}".format(self.Name, self.IsReady, self.Time, self.Score) def Update(self, input: BaseData) -> bool: self.queue.appendleft(input.Value) x = np.arange(len(self.queue)) log_ts = np.log(self.queue) slope, intercept, r_value, p_value, std_err = stats.linregress(x, log_ts) annualized_slope = (np.power(np.exp(slope), 252) - 1) * 100 self.Time = input.Time self.Score = float(annualized_slope * (r_value**2)) return x == self.queue.maxlen