| 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