Commodity Futures Trading Commission
Commitments of Traders
Introduction
The Commitments of Traders (COT) dataset by the Commodity Futures Trading Commission (CFTC) tracks aggregate trader positioning across US futures markets, broken down into long and short contracts held by each category of trader. The data covers 942 futures markets, starts in 1998, and is delivered on a weekly frequency. This dataset is created by QuantConnect processing and caching the CFTC Commitments of Traders reports.
For more information about the Commitments of Traders dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
The Commodity Futures Trading Commission (CFTC) is an independent agency of the US government, created by Congress in 1974, whose mission is to promote the integrity, resilience, and vibrancy of the US derivatives markets through sound regulation. The Commission regulates the US futures, options, and swaps markets, working to protect market participants and the public from fraud, manipulation, and abusive practices, and to foster open, transparent, competitive, and financially sound markets. As part of its oversight, the CFTC publishes the weekly Commitments of Traders report, which details the aggregate positions held by different categories of traders across the futures markets it regulates.
Requesting Data
To add CFTC Commitments of Traders data to your algorithm, call the AddData method. The dataset is unlinked, so instead of a security Symbol you pass a market from the CFTC.Markets helper. Save a reference to the dataset Symbol so you can access the data later in your algorithm.
The reports come in three types, each with its own class: CFTCLegacy, CFTCDisaggregated and CFTCFinancialFutures. Subscribe to the ones your strategy needs. Trader positioning is a signal, not a tradeable instrument, so add a separate tradeable security if you want to place orders.
class CFTCDataAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2020, 1, 1)
self.set_end_date(2020, 6, 1)
self.set_cash(100000)
self._spy = self.add_equity("SPY", Resolution.DAILY).symbol
self._legacy = self.add_data(CFTCLegacy, CFTC.Markets.E_MINI_SP_500, Resolution.DAILY).symbol
self._disaggregated = self.add_data(CFTCDisaggregated, CFTC.Markets.GOLD, Resolution.DAILY).symbol
self._tff = self.add_data(CFTCFinancialFutures, CFTC.Markets.E_MINI_SP_500, Resolution.DAILY).symbol public class CFTCDataAlgorithm : QCAlgorithm
{
private Symbol _spy, _legacy, _disaggregated, _tff;
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
_spy = AddEquity("SPY", Resolution.Daily).Symbol;
_legacy = AddData<CFTCLegacy>(CFTC.Markets.EMiniSP500, Resolution.Daily).Symbol;
_disaggregated = AddData<CFTCDisaggregated>(CFTC.Markets.Gold, Resolution.Daily).Symbol;
_tff = AddData<CFTCFinancialFutures>(CFTC.Markets.EMiniSP500, Resolution.Daily).Symbol;
}
}
Accessing Data
To get the current CFTC data, index the current Slice with the dataset Symbol. Slice objects deliver unique events to your algorithm as they happen, but the Slice may not contain data for your dataset at every time step. The reports are weekly, so most days carry no new data. To avoid issues, check if the Slice contains the data you want before you index it.
Position fields are nullable, since the CFTC leaves a category empty when it does not apply to a market. Check for a missing value before you act on it.
def on_data(self, slice):
if slice.contains_key(self._legacy):
data_point = slice[self._legacy]
net = data_point.non_commercial_net
if net is not None:
self.log(f"{self._legacy} non-commercial net at {slice.time}: {net}") public override void OnData(Slice slice)
{
if (slice.ContainsKey(_legacy))
{
var dataPoint = slice[_legacy];
var net = dataPoint.NonCommercialNet;
if (net != null)
{
Log($"{_legacy} non-commercial net at {slice.Time}: {net}");
}
}
}
To iterate through all of the dataset objects in the current Slice, call the Get method.
def on_data(self, slice):
for dataset_symbol, data_point in slice.get(CFTCDisaggregated).items():
self.log(f"{dataset_symbol} managed money net at {slice.time}: {data_point.managed_money_net}") public override void OnData(Slice slice)
{
foreach (var kvp in slice.Get<CFTCDisaggregated>())
{
var datasetSymbol = kvp.Key;
var dataPoint = kvp.Value;
Log($"{datasetSymbol} managed money net at {slice.Time}: {dataPoint.ManagedMoneyNet}");
}
}
Historical Data
To get historical CFTC data, call the History method with the dataset Symbol. If there is no data in the period you request, the history result is empty. The reports are weekly, so a request for 100 data points covers roughly two years.
# DataFrames legacy_history_df = self.history(self._legacy, 100, Resolution.DAILY) history_df = self.history([self._legacy, self._disaggregated, self._tff], 100, Resolution.DAILY) # Dataset objects legacy_history_bars = self.history[CFTCLegacy](self._legacy, 100, Resolution.DAILY) disaggregated_history_bars = self.history[CFTCDisaggregated](self._disaggregated, 100, Resolution.DAILY) tff_history_bars = self.history[CFTCFinancialFutures](self._tff, 100, Resolution.DAILY)
// Dataset objects
var legacyHistory = History<CFTCLegacy>(_legacy, 100, Resolution.Daily);
var disaggregatedHistory = History<CFTCDisaggregated>(_disaggregated, 100, Resolution.Daily);
var tffHistory = History<CFTCFinancialFutures>(_tff, 100, Resolution.Daily);
// Slice objects
var history = History(new[] {_legacy, _disaggregated, _tff}, 100, Resolution.Daily);
For more information about historical data, see History Requests.
Supported Markets
The following table shows the accessor code from the CFTC.Markets helper you need to add a market to your algorithm:
| Constant | Market |
|---|---|
CFTC.Markets.EMiniSP500CFTC.Markets.E_MINI_SP_500 | E-mini S&P 500 |
CFTC.Markets.GoldCFTC.Markets.GOLD | Gold |
CFTC.Markets.CrudeOilBrentCFTC.Markets.CRUDE_OIL_BRENT | Crude Oil, Brent |
CFTC.Markets.NaturalGasCFTC.Markets.NATURAL_GAS | Natural Gas |
CFTC.Markets.CornCFTC.Markets.CORN | Corn |
The table above is a sample. CFTC.Markets covers every market in the reports, and autocompletes as you type.
Not every trader category applies to every market, so a position field can be empty. Those fields are nullable, and you should check for a missing value before you use it.
Example Applications
The CFTC dataset lets you reach into the positioning behind the price. Examples include:
- Trading with the large speculators when the non-commercial net position turns positive, and against them when it turns negative.
- Fading crowded positioning by shorting a market once managed money net length reaches a multi-year extreme.
- Confirming a breakout by requiring commercial hedgers to be adding to the same side as the move.
- Rotating across commodities by ranking markets on the change in managed money net position from one report to the next.
- Building a leveraged-funds sentiment index across equity index and rate futures from the TFF report.
Classic Algorithm Example
The following example algorithm reads the Legacy report for the E-mini S&P 500 and trades SPY with the large speculators. When the non-commercial net position is long, the algorithm goes long SPY. When it turns short, the algorithm goes short. The COT is a signal, not a tradeable instrument, so the orders go to a separate security.
from AlgorithmImports import *
class CFTCDataAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2020, 1, 1)
self.set_end_date(2020, 6, 1)
self.set_cash(100000)
self._spy = self.add_equity("SPY", Resolution.DAILY).symbol
self._legacy = self.add_data(CFTCLegacy, CFTC.Markets.E_MINI_SP_500, Resolution.DAILY).symbol
history = self.history(CFTCLegacy, self._legacy, 60, Resolution.DAILY)
self.debug(f"We got {len(history)} items from our history request")
def on_data(self, slice):
legacy = slice.get(CFTCLegacy)
if not legacy or self._legacy not in legacy:
return
net = legacy[self._legacy].non_commercial_net
if net is None:
return
# Go long when the large speculators are net long the E-mini S&P 500
if net > 0:
self.set_holdings(self._spy, 1)
# Go short when they are net short
elif net < 0:
self.set_holdings(self._spy, -1) public class CFTCDataAlgorithm : QCAlgorithm
{
private Symbol _spy;
private Symbol _legacy;
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
_spy = AddEquity("SPY", Resolution.Daily).Symbol;
_legacy = AddData<CFTCLegacy>(CFTC.Markets.EMiniSP500, Resolution.Daily).Symbol;
var history = History<CFTCLegacy>(_legacy, 60, Resolution.Daily);
Debug($"We got {history.Count()} items from our history request");
}
public override void OnData(Slice slice)
{
var legacy = slice.Get<CFTCLegacy>();
if (!legacy.ContainsKey(_legacy))
{
return;
}
var net = legacy[_legacy].NonCommercialNet;
if (net == null)
{
return;
}
// Go long when the large speculators are net long the E-mini S&P 500
if (net > 0)
{
SetHoldings(_spy, 1);
}
// Go short when they are net short
else if (net < 0)
{
SetHoldings(_spy, -1);
}
}
}
Framework Algorithm Example
The following example algorithm buys SPY when the large speculators are net long the E-mini S&P 500 futures. When they turn net short, it short sells SPY.
from AlgorithmImports import *
class CFTCFrameworkAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2020, 1, 1)
self.set_end_date(2020, 6, 1)
self.set_cash(100000)
self.universe_settings.resolution = Resolution.DAILY
# We trade SPY as the proxy of the E-mini S&P 500 futures the report covers
symbols = [Symbol.create("SPY", SecurityType.EQUITY, Market.USA)]
self.add_universe_selection(ManualUniverseSelectionModel(symbols))
# A custom alpha model that emits trade insights based on CFTC positioning data
self.add_alpha(CFTCAlphaModel(self))
# Equally invest to dissipate non-systematic capital concentration risk on individual stock
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
class CFTCAlphaModel(AlphaModel):
def __init__(self, algorithm: QCAlgorithm) -> None:
self.tradable_symbols = []
# Request the Legacy report for the E-mini S&P 500 for trade signal generation
self.cftc_symbol = algorithm.add_data(CFTCLegacy, CFTC.Markets.E_MINI_SP_500, Resolution.DAILY).symbol
# Historical data
history = algorithm.history(CFTCLegacy, self.cftc_symbol, 60, Resolution.DAILY)
algorithm.debug(f"We got {len(history)} items from our history request")
def update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]:
# The COT is published weekly, so most days carry no new report
points = slice.get(CFTCLegacy)
if self.cftc_symbol not in points:
return []
# Non-commercial traders are the large speculators, the "smart money" of the report
net = points[self.cftc_symbol].non_commercial_net
if net is None:
return []
insights = []
for symbol in self.tradable_symbols:
# Buy when the large speculators are net long, following the positioning of the funds
if net > 0:
insights += [Insight.price(symbol, timedelta(days=7), InsightDirection.UP)]
# Short sell when they are net short
elif net < 0:
insights += [Insight.price(symbol, timedelta(days=7), InsightDirection.DOWN)]
return insights
def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
for security in changes.added_securities:
self.tradable_symbols.append(security.symbol) public class CFTCFrameworkAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
UniverseSettings.Resolution = Resolution.Daily;
// We trade SPY as the proxy of the E-mini S&P 500 futures the report covers
var symbols = new[] {QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA)};
AddUniverseSelection(new ManualUniverseSelectionModel(symbols));
// A custom alpha model that emits trade insights based on CFTC positioning data
AddAlpha(new CFTCAlphaModel(this));
// Equally invest to dissipate non-systematic capital concentration risk on individual stock
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
}
}
public class CFTCAlphaModel : AlphaModel
{
private Symbol CFTCSymbol;
private List<Symbol> tradableSymbols = new List<Symbol>();
public CFTCAlphaModel(QCAlgorithm algorithm)
{
// Request the Legacy report for the E-mini S&P 500 for trade signal generation
CFTCSymbol = algorithm.AddData<CFTCLegacy>(CFTC.Markets.EMiniSP500, Resolution.Daily).Symbol;
// Historical data
var history = algorithm.History<CFTCLegacy>(CFTCSymbol, 60, Resolution.Daily);
algorithm.Debug($"We got {history.Count()} items from our history request");
}
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice slice)
{
var insights = new List<Insight>();
// The COT is published weekly, so most days carry no new report
var points = slice.Get<CFTCLegacy>();
if (!points.ContainsKey(CFTCSymbol))
{
return insights;
}
// Non-commercial traders are the large speculators, the "smart money" of the report
var net = points[CFTCSymbol].NonCommercialNet;
if (net == null)
{
return insights;
}
foreach (var symbol in tradableSymbols)
{
// Buy when the large speculators are net long, following the positioning of the funds
if (net > 0)
{
insights.Add(Insight.Price(symbol, TimeSpan.FromDays(7), InsightDirection.Up));
}
// Short sell when they are net short
if (net < 0)
{
insights.Add(Insight.Price(symbol, TimeSpan.FromDays(7), InsightDirection.Down));
}
}
return insights;
}
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
tradableSymbols.Add(security.Symbol);
}
}
}
Data Point Attributes
The CFTC Commitments of Traders dataset provides CFTCLegacy, CFTCDisaggregated and CFTCFinancialFutures objects.
CFTCLegacy
CFTCLegacy objects have the following attributes:
CFTCDisaggregated
CFTCDisaggregated objects have the following attributes:
CFTCFinancialFutures
CFTCFinancialFutures objects have the following attributes: