# How to Implement the Time Series Momentum (TSMOM) Strategy in Python

> Implement the Time Series Momentum TSMOM strategy in Python. Learn to invest in assets with positive returns and short negative ones with inverse-volatility weighting for target portfolio volatility.

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

---

**The Time Series Momentum (TSMOM) strategy invests in assets with positive 12-month returns while shorting those with negative returns, applying inverse-volatility weighting to target 10% annualized portfolio volatility.**

The Time Series Momentum (TSMOM) strategy, first formalized by Moskowitz, Ooi & Pedersen (2012), captures absolute momentum across diversified futures contracts by taking long positions in past winners and short positions in past losers. The **awesome-systematic-trading** repository provides a complete, production-ready implementation of this academic approach as a QuantConnect algorithm in [`static/strategies/time-series-momentum-effect.py`](https://github.com/paperswithbacktest/awesome-systematic-trading/blob/main/static/strategies/time-series-momentum-effect.py). This guide examines the architecture, signal generation mechanics, and risk management systems that transform the paper's theoretical framework into executable Python code.

## TSMOM Strategy Architecture Overview

The implementation follows a modular architecture designed for QuantConnect's event-driven backtesting engine. The `TimeSeriesMomentum` class inherits from `QCAlgorithm` and manages a universe of 70+ futures contracts spanning commodities, currencies, equity indices, and bond futures.

### Universe Definition and Data Ingestion

The strategy defines its tradeable universe in `self.symbols` (lines 24-85), encompassing major futures markets from the CME, ICE, and other exchanges. Data ingestion leverages a custom `QuantpediaFutures` class (lines 204-232) that reads back-adjusted daily price series from Quantpedia's public data repository.

```python

# From Initialize method (line ~101)

self.AddData(QuantpediaFutures, symbol, Resolution.Daily)

```

Each symbol maintains a `RollingWindow[float]` storing exactly 12 months of historical prices (approximately 252 trading days), initialized at line 106:

```python
self.data[symbol] = RollingWindow[float](self.period)  # self.period = 252

```

### Signal Generation Mechanics

The core TSMOM signal calculates the 12-month total return by comparing the most recent back-adjusted price to the price 12 months prior. Implemented in line 136:

```python
performance = back_adjusted_prices[0] / back_adjusted_prices[-1] - 1

```

Assets with positive performance enter the long portfolio, while those with negative performance become shorts. This binary classification occurs during the monthly rebalancing cycle triggered when `self.recent_month != self.Time.month`.

### Volatility Estimation and Weighting

Position sizing employs inverse-volatility weighting derived from 60-day historical volatility (line 147):

```python
volatility_3M = np.std(daily_rets) * sqrt(252)

```

The strategy calculates separate volatility-adjusted weights for the long and short legs, then applies portfolio-level volatility targeting to achieve 10% annualized volatility (`self.targeted_volatility = 0.10`).

## Core Implementation Components

### The Initialize Method Setup

The `Initialize` method configures the algorithm's parameters and warm-up period. Critical settings include:

- **Leverage Cap**: Maximum 4x portfolio leverage (line 186)
- **Warm-up Period**: 252 trading days to populate rolling windows
- **Fee Model**: Custom 0.5 basis point transaction cost structure

```python
def Initialize(self):
    self.SetStartDate(2000, 1, 1)
    self.SetCash(10000000)
    self.targeted_volatility = 0.10
    self.leverage_cap = 4.0
    self.period = 12 * 21  # ~252 trading days

    
    # Set warm-up to populate RollingWindows

    self.SetWarmUp(self.period, Resolution.Daily)

```

### Monthly Rebalancing and Execution Logic

The `OnData` method triggers rebalancing on the first trading day of each new month. The execution workflow (lines 190-202) involves:

1. Liquidating positions no longer in the long/short sets
2. Calculating inverse-volatility weights for qualifying assets
3. Determining portfolio leverage based on volatility targeting
4. Executing trades via `SetHoldings`

The leverage calculation (lines 182-187) ensures the volatility-targeted exposure respects the 4x cap:

```python
leverage = self.targeted_volatility / portfolio_volatility
leverage = min(self.leverage_cap, leverage)  # Line 186

```

### Custom Fee Model Implementation

Transaction costs are modeled using the `CustomFeeModel` class (lines 332-336), applying a conservative 0.5 basis point commission:

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

```

## Practical Implementation Examples

### Running the Algorithm on QuantConnect

Deploy the strategy by inheriting from the `TimeSeriesMomentum` class or running it directly:

```python
from AlgorithmImports import *

class MyTSMOM(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(10_000_000)
        
        # Initialize the TSMOM strategy

        self.tsmom = TimeSeriesMomentum()
        self.tsmom.Initialize()

```

### Extracting Individual Asset Signals

Access the 12-month momentum signal for specific contracts after warm-up completion:

```python
symbol = "CME_CL1"  # Crude Oil Futures

window = self.data[symbol]
prices = np.array([x for x in window])

# Calculate TSMOM signal

tsmom_signal = prices[0] / prices[-1] - 1
print(f"{symbol} 12-month return: {tsmom_signal:.2%}")

```

### Customizing Strategy Parameters

Modify the look-back period or volatility target to test variations:

```python

# Extend look-back to 18 months (~378 trading days)

self.period = 18 * 21
self.SetWarmUp(self.period, Resolution.Daily)

# Increase volatility target to 15%

self.targeted_volatility = 0.15

```

### Expanding the Asset Universe

Add new futures contracts by extending `self.symbols` in the `Initialize` method:

```python

# Add cryptocurrency futures

self.symbols.append("CME_BTC1")

```

## Summary

- The **awesome-systematic-trading** repository implements TSMOM in [`static/strategies/time-series-momentum-effect.py`](https://github.com/paperswithbacktest/awesome-systematic-trading/blob/main/static/strategies/time-series-momentum-effect.py) as a complete QuantConnect algorithm trading 70+ futures contracts.
- **Signal generation** uses 12-month (`self.period = 252`) total return calculations stored in `RollingWindow` objects, with positive performers going long and negative performers going short.
- **Risk management** combines inverse-volatility weighting (based on 60-day historical volatility) with portfolio-level volatility targeting (10% annualized) and a 4x leverage cap.
- **Execution** occurs monthly, utilizing the `CustomFeeModel` (0.5 bps) and `QuantpediaFutures` data handler for back-adjusted continuous contract prices.

## Frequently Asked Questions

### How does the TSMOM implementation differ from the original academic paper?

The repository implementation follows Moskowitz, Ooi & Pedersen (2012) closely but substitutes the paper's GARCH volatility estimation with a simpler 60-day historical standard deviation (`np.std(daily_rets) * sqrt(252)`). This pragmatic simplification reduces computational complexity while maintaining the strategy's risk-adjusted return characteristics.

### What data source does the algorithm use for futures prices?

The strategy utilizes Quantpedia's public futures data via the custom `QuantpediaFutures` class (lines 204-232), which reads back-adjusted daily CSV files. The `GetSource` method constructs URLs pointing to Quantpedia's data repository, while the `Reader` method parses price data into QuantConnect's data format.

### Why is the leverage capped at 4x?

The 4x leverage cap (`self.leverage_cap = 4.0`) implemented in line 186 serves as a circuit breaker to prevent excessive exposure during periods of abnormally low volatility estimates. This risk control ensures that even when historical volatility collapses (potentially inflating position sizes under the volatility-targeting regime), gross exposure remains bounded.

### How can I modify the rebalancing frequency?

The current implementation rebalances monthly by checking `if self.recent_month != self.Time.month` in the `OnData` method. To change frequency, modify the conditional logic to trigger on different time intervals (e.g., weekly using `self.Time.isoweekday()`) and adjust the volatility calculation window accordingly to match the rebalancing period.