Overall Statistics |
Total Orders 613 Average Win 0.32% Average Loss 0.00% Compounding Annual Return 18.545% Drawdown 27.600% Expectancy 58.853 Start Equity 100000 End Equity 742336.64 Net Profit 642.337% Sharpe Ratio 0.689 Sortino Ratio 0.711 Probabilistic Sharpe Ratio 13.010% Loss Rate 16% Win Rate 84% Profit-Loss Ratio 70.31 Alpha 0.038 Beta 0.967 Annual Standard Deviation 0.179 Annual Variance 0.032 Information Ratio 0.3 Tracking Error 0.118 Treynor Ratio 0.128 Total Fees $207.36 Estimated Strategy Capacity $810000000.00 Lowest Capacity Asset HPE W584BQZL94KL Portfolio Turnover 0.06% |
# region imports from AlgorithmImports import * from datetime import datetime, timedelta from collections import deque import math # endregion class ETFConstituentUniverseROCPAlphaModelAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2013, 1, 7) self.SetCash(100000) self.SetAlpha(ConstituentWeightedROCPAlphaModel()) self.SetPortfolioConstruction(FixedDollarValuePortfolioConstructionModel(self, 3000)) self.SetExecution(ImmediateExecutionModel()) iwb = self.AddEquity("IWB", Resolution.Daily).Symbol # IWB is the iShares Russell 1000 ETF self.UniverseSettings.Resolution = Resolution.Daily self.Settings.MinimumOrderMarginPortfolioPercentage = 0.01 self.AddUniverse(self.Universe.ETF(iwb, self.UniverseSettings, self.FilterETFConstituents)) def FilterETFConstituents(self, constituents): return [i.Symbol for i in constituents if i.Weight is not None and i.Weight >= 0.001] class ConstituentWeightedROCPAlphaModel(AlphaModel): def __init__(self): self.rocp_symbol_data = {} self.entry_signals = {} self.trade_start_time = {} def Update(self, algorithm: QCAlgorithm, data: Slice): insights = [] for symbol, symbol_data in self.rocp_symbol_data.items(): if symbol not in data.Bars: continue bar = data.Bars[symbol] symbol_data.Update(bar) if not symbol_data.rocp.IsReady or not symbol_data.atr.IsReady: continue current_roc = symbol_data.rocp.Current.Value yesterday_roc = symbol_data.previous_roc if symbol not in self.entry_signals: if current_roc < -20 and current_roc > yesterday_roc: self.entry_signals[symbol] = True algorithm.Debug(f"Entry signal for {symbol}: ROC = {current_roc:.2f}, Yesterday ROC = {yesterday_roc:.2f}") elif self.entry_signals[symbol]: entry_price = bar.Open profit_target = entry_price + symbol_data.atr.Current.Value stop_loss = entry_price - 2.5 * symbol_data.atr.Current.Value insights.append(Insight.Price( symbol, timedelta(days=20), InsightDirection.Up, abs(current_roc), None )) self.entry_signals[symbol] = False self.trade_start_time[symbol] = algorithm.Time algorithm.Debug(f"Entering trade for {symbol} at {entry_price:.2f}, PT: {profit_target:.2f}, SL: {stop_loss:.2f}") elif symbol in self.trade_start_time: if (algorithm.Time - self.trade_start_time[symbol]).days >= 20: insights.append(Insight.Price( symbol, timedelta(days=1), InsightDirection.Flat, 0, None )) del self.trade_start_time[symbol] algorithm.Debug(f"Exiting trade for {symbol} due to 20-day hold period") return insights def OnSecuritiesChanged(self, algorithm, changes): for added in changes.AddedSecurities: if added.Symbol not in self.rocp_symbol_data: self.rocp_symbol_data[added.Symbol] = SymbolData(added.Symbol, algorithm, 14) for removed in changes.RemovedSecurities: if removed.Symbol in self.rocp_symbol_data: del self.rocp_symbol_data[removed.Symbol] if removed.Symbol in self.entry_signals: del self.entry_signals[removed.Symbol] if removed.Symbol in self.trade_start_time: del self.trade_start_time[removed.Symbol] class SymbolData: def __init__(self, symbol, algorithm, period): self.symbol = symbol self.rocp = algorithm.ROCP(symbol, period) self.atr = algorithm.ATR(symbol, period) self.previous_values = deque(maxlen=2) self.previous_roc = 0 def Update(self, bar): self.rocp.Update(bar.EndTime, bar.Close) self.atr.Update(bar) self.previous_values.append(self.rocp.Current.Value) if len(self.previous_values) == 2: self.previous_roc = self.previous_values[0] class FixedDollarValuePortfolioConstructionModel(PortfolioConstructionModel): def __init__(self, algorithm, fixed_dollar_value): self.algorithm = algorithm self.fixed_dollar_value = fixed_dollar_value self.targets = {} def CreateTargets(self, algorithm, insights): targets = [] for insight in insights: if insight.Direction == InsightDirection.Up: self.targets[insight.Symbol] = self.fixed_dollar_value elif insight.Direction == InsightDirection.Flat: if insight.Symbol in self.targets: del self.targets[insight.Symbol] for symbol, target_value in self.targets.items(): quantity = math.floor(target_value / algorithm.Securities[symbol].Price) targets.append(PortfolioTarget(symbol, quantity)) return targets