Skip to content

Timeseries Data Guide

The Timeseries endpoint (client.timeseries.get()) provides programmatic access to historical weekly positioning data published by the U.S. Commodity Futures Trading Commission (CFTC).

cotbias allows you to query up to 156 weeks (3 years) of historical weekly data across multiple CFTC report families, returning either analysis-ready Pandas DataFrames or typed Pydantic models.


⚡ Quick Example

from cotbias import CotClient

with CotClient() as client:
    # 52 weeks of E-mini S&P 500 combined positioning
    df = client.timeseries.get("ES", lookback_weeks=52, as_dataframe=True)

print(df[["commercial_net", "noncommercial_net", "cot_52w_commercial"]].tail())

🛠 Parameters Reference

client.timeseries.get(
    symbol: str,
    contract_type: str = "futopt",
    lookback_weeks: int = 156,
    report: str = "legacy",
    as_dataframe: bool = True,
)
Parameter Type Default Description
symbol str Required Market ticker symbol (e.g. 'ES', 'NQ', 'GC', 'CL', '6E', 'ZB'). Case-insensitive.
contract_type str 'futopt' Position breakdown: 'fut' (Futures only), 'futopt' (Combined Futures & Options), or 'opt' (Options delta).
lookback_weeks int 156 Number of weekly reports to retrieve (1 to 156 weeks).
report str 'legacy' CFTC report classification family: 'legacy', 'disaggregated', 'tff', or 'supplemental'.
as_dataframe bool True Return a DatetimeIndex-sorted Pandas DataFrame (True) or a typed Pydantic model (False).

📊 CFTC Report Families

The CFTC categorizes market participants differently across four official report types:

1. Legacy Report (report='legacy')

The historical benchmark report dividing participants into two primary groups: - Commercials: Hedgers, producers, merchants, and swap dealers who use futures to hedge business risk. - Non-Commercials: Speculators, hedge funds, commodity trading advisors (CTAs), and asset managers. - Non-Reportables: Small retail retail traders below reportable CFTC position thresholds.

2. Disaggregated Report (report='disaggregated')

Standard for physical commodities (Crude Oil, Gold, Corn, Wheat, Natural Gas): - Producer / Merchant / Processor / User - Swap Dealers - Managed Money (Hedge funds, CTAs) - Other Reportables

3. Financial Traders Report (report='tff')

Standard for financial futures (Equities, FX, US Treasury bonds, Eurodollars): - Dealer / Intermediary (Sell-side banks and market makers) - Asset Manager / Institutional (Pension funds, mutual funds, insurance) - Leveraged Funds (Hedge funds, proprietary trading desks) - Other Reportables

4. Supplemental / CIT (report='supplemental')

Tracks Index Trader activity across 12 selected agricultural and soft commodity contracts.


📐 Key Analytical Metrics

Every timeseries record includes computed quantitative indicators alongside raw position sizes:

1. 52-Week COT Index (cot_52w_commercial)

Normalizes current commercial net positioning against its 52-week rolling minimum and maximum:

$$\text{COT Index} = \frac{\text{Net Position}t - \text{Min}}}{\text{Max{52w} - \text{Min} \times 100$$}

  • Values > 80: Commercials are holding historically heavy net-long hedges (historically bullish signal for the underlying asset).
  • Values < 20: Commercials are holding historically heavy net-short hedges (historically bearish/crowded signal).

2. 3-Year Rolling Z-Score (z_score_156w_commercial)

Measures how many standard deviations the current positioning deviates from its 3-year mean:

$$Z = \frac{\text{Net Position}t - \mu$$}}{\sigma_{156w}

  • $Z > +2.0$: Extreme bullish institutional accumulation.
  • $Z < -2.0$: Extreme bearish institutional distribution.

3. Hedging Pressure (commercial_hedging_pressure)

The ratio of commercial short positions to total commercial participation, indicating whether producers are aggressively locking in forward sales.

4. Top-4 Concentration Ratio (conc_top4_net_short)

Percentage of total open interest controlled by the 4 largest net-short institutional traders. Extreme values (> 40%) indicate vulnerability to short squeeze dynamics.


📈 Working with Pandas DataFrames

When as_dataframe=True (default), the returned DataFrame is automatically: 1. Converted to a pd.DatetimeIndex on report_date. 2. Sorted chronologically in ascending order (oldest to newest).

import matplotlib.pyplot as plt
from cotbias import CotClient

with CotClient() as client:
    df = client.timeseries.get("GC", lookback_weeks=104, as_dataframe=True)

# Plot Gold Commercial vs Non-Commercial positioning
fig, ax1 = plt.subplots(figsize=(12, 6))

ax1.plot(df.index, df["commercial_net"] / 1000, label="Commercials (k contracts)", color="#e74c3c")
ax1.plot(df.index, df["noncommercial_net"] / 1000, label="Speculators (k contracts)", color="#2ecc71")
ax1.set_ylabel("Net Positioning (Thousands)")
ax1.axhline(0, color="gray", linestyle="--", alpha=0.6)
ax1.legend(loc="upper left")
ax1.set_title("Gold (GC) Institutional COT Positioning")
plt.tight_layout()
plt.show()

🧩 Working with Pydantic Models

If you prefer strongly typed objects for strict serialization, pass as_dataframe=False:

from cotbias import CotClient
from cotbias.models import TimeseriesResponse

with CotClient() as client:
    res: TimeseriesResponse = client.timeseries.get("CL", lookback_weeks=12, as_dataframe=False)

print(f"Contract: {res.symbol} | CFTC #{res.cftc_code}")
print(f"Total Weeks: {res.count}")

latest_week = res.data[-1]
print(f"Date: {latest_week.report_date}")
print(f"Speculator Net: {latest_week.noncommercial_net:,} contracts")