Blog

The Commitments of Traders report: using CFTC positioning data in a trading algorithm

· 7 min read · Data, Futures, Python

Every week the US Commodity Futures Trading Commission publishes the Commitments of Traders (COT) report: how many futures and options contracts different groups of traders hold, long and short, in each US futures market with enough large traders to report. It's one of the few free, long-running views of who is positioned how, across stock indices, rates, currencies, energy, metals and agricultural markets.

What's in the report

The CFTC publishes the same positions grouped in three ways:

  • Legacy: commercial traders (who hedge a business) against non-commercial traders (largely speculators), plus small non-reportable positions. The longest history.
  • Disaggregated, for physical commodities: producers and merchants, swap dealers, managed money and other reportable traders.
  • Traders in Financial Futures, for financial markets: dealers and intermediaries, asset managers and institutional investors, leveraged funds, and other reportable traders.

Each row gives long and short positions, spreads, changes from the previous week and open interest. Most strategies use a group's net position, long minus short, usually divided by open interest so markets of different sizes compare.

The timing trap

The report describes positions at the close of Tuesday, but it's released on Friday at 3:30 p.m. Eastern, and later when a federal holiday falls in the week. Many datasets stamp each report with its Tuesday date. A backtest using those dates trades on positioning three days before anyone could see it, a textbook case of look-ahead bias, and for a weekly signal three days is a large share of the holding period.

On MarketsCoder the Commitments of Traders dataset delivers each report at its actual release time, holidays included, so the algorithm sees it when the market did.

A positioning strategy in Python

This example reads the Traders in Financial Futures report for E-mini S&P 500 futures and holds SPY while asset managers are more net long than their average over the past year.

from AlgorithmImports import *
from lean_platform_data import CotReport


class PositioningAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2010, 1, 1)
        self.set_end_date(2021, 1, 1)
        self.spy = self.add_equity("SPY", Resolution.DAILY).symbol
        # A futures root (ES, GC, CL, 6E, ...) or a CFTC contract code.
        self.cot = self.add_data(CotReport, "ES").symbol
        self.history = RollingWindow[float](52)  # a year of weekly reports

    def on_data(self, slice):
        if self.cot not in slice:
            return
        c = slice[self.cot]
        net = (c.asset_manager_long - c.asset_manager_short) / c.open_interest
        self.history.add(net)
        if not self.history.is_ready:
            return
        average = sum(self.history) / self.history.count
        if net > average:
            self.set_holdings(self.spy, 1)
        else:
            self.liquidate(self.spy)

RollingWindow keeps the last 52 values, so the comparison is always against the trailing year, using only reports that had already been published.

Ideas to explore

  • Extremes, not levels. Many traders look at where net positioning sits in its multi-year range, for example as a percentile, and treat extremes as a sign of a crowded trade.
  • Who, not just how much. Commercial hedgers and speculators often lean opposite ways. The spread between groups can say more than either alone.
  • Changes. The week-on-week change in a group's net position is a faster-moving signal than its level.
  • Confirmation. Positioning is slow. It's often used as a filter on a price-based signal rather than a trigger by itself.

Whatever you try, the weekly frequency means few independent observations, so keep rules simple and test on held-out years. Our post on overfitting covers how.

Test your own idea on point-in-time data.

Write a strategy in Python or describe it to the AI assistant, and see a full backtest report in minutes.

Get started