| Overall Statistics |
|
Total Orders 267 Average Win 1.50% Average Loss -1.34% Compounding Annual Return 7.697% Drawdown 15.600% Expectancy 0.232 Start Equity 200000 End Equity 289804.41 Net Profit 44.902% Sharpe Ratio 0.167 Sortino Ratio 0.192 Probabilistic Sharpe Ratio 8.840% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.12 Alpha -0.012 Beta 0.526 Annual Standard Deviation 0.117 Annual Variance 0.014 Information Ratio -0.355 Tracking Error 0.112 Treynor Ratio 0.037 Total Fees $1509.09 Estimated Strategy Capacity $100000000.00 Lowest Capacity Asset XLB RGRPZX100F39 Portfolio Turnover 3.63% Drawdown Recovery 605 |
from AlgorithmImports import *
class EquitySectorRotationMomentum(QCAlgorithm):
def initialize(self):
self.set_start_date(self.end_date - timedelta(5 * 365))
self.set_cash(200000)
self.settings.seed_initial_prices = True
self.settings.automatic_indicator_warm_up = True
self._tickers = [
"XLK", "XLF", "XLE", "XLV", "XLI", "XLB",
"XLY", "XLP", "XLU", "XLRE", "XLC"
]
self._roc_period = 63
self._top_count = 3
self._rocs = {}
self._volatilities = {}
self._entry_prices = {}
for ticker in self._tickers:
equity = self.add_equity(ticker, Resolution.DAILY)
self._rocs[equity.symbol] = self.roc(equity.symbol, self._roc_period, Resolution.DAILY)
self._volatilities[equity.symbol] = self.std(equity.symbol, 20, Resolution.DAILY)
self._spy = self.add_equity("SPY", Resolution.DAILY)
self._spy_roc = self.roc(self._spy.symbol, self._roc_period, Resolution.DAILY)
self._shy = self.add_equity("SHY", Resolution.DAILY)
self.schedule.on(
self.date_rules.month_start('SPY'),
self.time_rules.at(8, 0),
self._rebalance
)
self.schedule.on(
self.date_rules.every_day('SPY'),
self.time_rules.before_market_close('SPY', 5),
self._check_stops
)
def _rebalance(self):
if not all(r.is_ready for r in self._rocs.values()):
return
if not self._spy_roc.is_ready:
return
spy_roc = self._spy_roc.current.value
scores = []
for symbol, roc in self._rocs.items():
rel_strength = roc.current.value - spy_roc
scores.append((symbol, rel_strength))
scores.sort(key=lambda x: x[1], reverse=True)
top = scores[:self._top_count]
top_symbols = [s[0] for s in top]
top_scores = [s[1] for s in top]
avg_rs = sum(top_scores) / len(top_scores)
if avg_rs <= 0:
self.liquidate()
self.set_holdings(self._shy.symbol, 1.0)
return
vols = {}
for symbol in top_symbols:
vols[symbol] = max(self._volatilities[symbol].current.value, 1e-6)
inv_vols = {s: 1.0 / vols[s] for s in top_symbols}
total_inv = sum(inv_vols.values())
weights = {s: inv_vols[s] / total_inv for s in top_symbols}
targets = [PortfolioTarget(s, weights[s]) for s in top_symbols]
self.set_holdings(targets, liquidate_existing_holdings=True)
for symbol in top_symbols:
self._entry_prices[symbol] = self.securities[symbol].price
def _check_stops(self):
for symbol, entry_price in list(self._entry_prices.items()):
if self.portfolio[symbol].invested:
current_price = self.securities[symbol].price
if current_price < entry_price * 0.95:
self.liquidate(symbol)
del self._entry_prices[symbol]