# How to Implement a Sector Momentum Rotational System in QuantConnect

> Implement a sector momentum rotational system in QuantConnect. Learn to rank ETFs by 12 month momentum and rebalance monthly for persistent relative strength.

- Repository: [Papers With Backtest/awesome-systematic-trading](https://github.com/paperswithbacktest/awesome-systematic-trading)
- Tags: how-to-guide
- Published: 2026-08-01

---

**A sector momentum rotational system ranks sector ETFs by 12‑month price momentum, holds an equally‑weighted portfolio of the top three performers, and rebalances monthly to capture persistent relative strength across business cycles.**

The sector momentum rotational system is a quantitative strategy that exploits the persistence of relative strength among sector ETFs. According to the implementation in the `paperswithbacktest/awesome-systematic-trading` repository, this approach selects the strongest sectors based on 12‑month Rate‑of‑Change (ROC) and rotates capital monthly into the top three performers.

## Core Architecture of the Sector Momentum Strategy

### Algorithm Class and Initialization

The `SectorMomentumAlgorithm` class inherits from `QCAlgorithm` and defines the lifecycle methods `Initialize`, `OnData`, and `OnROCUpdated`. In `Initialize`, the algorithm sets a warm‑up period of approximately 252 trading days (`self.period = 12 * 21`) to populate the momentum indicators before trading begins.

The strategy tracks ten sector ETFs: **VNQ**, **XLK**, **XLE**, **XLV**, **XLF**, **XLI**, **XLB**, **XLY**, **XLP**, and **XLU**. Each symbol receives a daily ROC calculation stored in the `self.data` dictionary.

### Momentum Indicator Setup

The algorithm utilizes `self.ROC(symbol, self.period, Resolution.Daily)` to compute 12‑month momentum for each ETF. These indicators attach to the daily price stream and update automatically. The system subscribes to the first symbol’s `Updated` event to detect calendar transitions efficiently.

### Monthly Rebalance Trigger

The `OnROCUpdated` method detects month changes by comparing `self.Time.month` against a stored `self.recent_month` variable. When the month changes, it sets `self.rebalance_flag = True`, signaling that rebalancing should occur on the next data tick.

## Step-by-Step Implementation

### 1. Initialize the Algorithm and Attach Indicators

```python
class SectorMomentumAlgorithm(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(100_000)

        # period = 12 months (≈252 trading days)

        self.period = 12 * 21
        self.SetWarmUp(self.period)

        # Ten sector ETFs

        self.symbols = [
            "VNQ", "XLK", "XLE", "XLV", "XLF",
            "XLI", "XLB", "XLY", "XLP", "XLU",
        ]

        # Attach ROC indicator and custom fee model

        self.data = {}
        for symbol in self.symbols:
            equity = self.AddEquity(symbol, Resolution.Daily)
            equity.SetFeeModel(CustomFeeModel())
            equity.SetLeverage(5)
            self.data[symbol] = self.ROC(symbol, self.period, Resolution.Daily)

        # Subscribe to the first ROC to know when a new month begins

        self.data[self.symbols[0]].Updated += self.OnROCUpdated
        self.recent_month = -1
        self.rebalance_flag = False

```

### 2. Detect Month Changes

```python
def OnROCUpdated(self, sender, updated):
    if self.recent_month != self.Time.month:
        self.recent_month = self.Time.month
        self.rebalance_flag = True

```

### 3. Execute Monthly Rebalance Logic

```python
def OnData(self, data):
    if self.IsWarmingUp:
        return

    if self.rebalance_flag:
        self.rebalance_flag = False

        # Rank ETFs by 12‑month ROC

        ranked = sorted(
            [(sym, roc) for sym, roc in self.data.items()
             if roc.IsReady and sym in data and data[sym]],
            key=lambda tup: tup[1].Current.Value,
            reverse=True,
        )
        top_three = [sym for sym, _ in ranked[:3]]

        # Liquidate losers

        for holding in [h for h in self.Portfolio if self.Portfolio[h].Invested]:
            if holding not in top_three:
                self.Liquidate(holding)

        # Equal‑weight winners

        weight = 1.0 / len(top_three)
        for sym in top_three:
            self.SetHoldings(sym, weight)

```

### 4. Model Realistic Transaction Costs

```python
class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
        return OrderFee(CashAmount(fee, "USD"))

```

## Key Implementation Details

### Security Selection and Ranking

The algorithm filters for ready indicators using `roc.IsReady` and sorts the universe by `Current.Value` in descending order. It selects the top three symbols and liquidates any existing holdings not in this ranked list. This ensures the portfolio always holds the highest‑momentum sectors while eliminating relative underperformers.

### Position Sizing and Execution

Winning sectors receive equal weight allocations calculated as `weight = 1.0 / len(top_three)`, typically 33.33% per position. The `SetHoldings` method executes these target allocations proportionally to portfolio value, while `Liquidate` closes positions in falling sectors immediately.

### Transaction Cost Modeling

The `CustomFeeModel` applies a realistic commission of **0.5 basis points per share** using the formula `price × quantity × 0.00005`. This accounts for friction costs that erode momentum profits, particularly important given the strategy’s monthly turnover.

## Source Code Reference

The complete implementation resides in [`sector-momentum-rotational-system.py`](https://github.com/paperswithbacktest/awesome-systematic-trading/blob/main/sector-momentum-rotational-system.py) within the `static/strategies/` directory of the repository. This file contains the full `SectorMomentumAlgorithm` class and can be deployed directly to QuantConnect or local Lean environments without modification.

## Summary

- The strategy measures 12‑month price momentum using ROC indicators for ten sector ETFs.
- Monthly rebalancing selects the top three performers and allocates equal capital to each.
- The `OnROCUpdated` method ensures rebalancing occurs only on the first trading day of each new month.
- A custom fee model simulates 0.5 bps commissions to improve backtest realism.
- Full source code is available at [`paperswithbacktest/awesome-systematic-trading/static/strategies/sector-momentum-rotational-system.py`](https://github.com/paperswithbacktest/awesome-systematic-trading/blob/main/paperswithbacktest/awesome-systematic-trading/static/strategies/sector-momentum-rotational-system.py).

## Frequently Asked Questions

### Why use a 12‑month lookback period for momentum?

The 12‑month (approximately 252 trading days) lookback captures the "intermediate-term" momentum anomaly documented in academic literature. This period is long enough to filter out short‑term noise while identifying persistent trends in sector performance that typically last several months.

### How does the algorithm handle the monthly rebalancing timing?

The `OnROCUpdated` callback monitors the ROC indicator of the first symbol in the universe. When `self.Time.month` differs from the stored `self.recent_month`, it triggers `self.rebalance_flag`. The actual execution occurs in the next `OnData` iteration, ensuring all indicators are updated with the latest closing prices before ranking and trading.

### What is the impact of the CustomFeeModel on strategy performance?

The `CustomFeeModel` charges 0.5 basis points per share traded, which translates to roughly 0.005% of trade value. While sector ETFs are relatively liquid, monthly rotation among three positions generates approximately 36 round‑trip trades annually, making realistic commission modeling essential for accurate Sharpe ratio estimation (reported at approximately 0.40 in the repository documentation).

### Can this rotational system be adapted for other asset classes?

Yes. The architecture supports any liquid instruments with price history. Replace the sector ETF symbols in `self.symbols` with asset classes such as international equities, fixed income ETFs, or commodity pools. Ensure you adjust `self.period` if using different timeframe momentum (e.g., 6‑month for faster rotation) and maintain sufficient warm‑up data for indicator calculation.