Sector Flow & Momentum

Flow-based sector signal that combines 4-week speculative flows, crowding alignment, and flow momentum to proxy economic cycle dynamics across Energy, Metals, Ags, FX, and Rates.

Abstract

This signal aggregates 4-week speculative flows across futures markets into sector-level flow and momentum metrics. By combining sign and magnitude of flows with crowding information, it distinguishes between trend-confirming flows, contrarian flows, and the evolution of flow momentum over time. The output provides an economic-cycle proxy for sectors such as industrial metals, energy, and agriculture, highlighting phases of expansion, slowdown, and potential turning points.

1. Inputs

2. Sector Classification

Markets are first mapped into macro sectors, preferring explicit metadata fields and falling back to name-based heuristics when necessary. The sectors are:

This mirrors the sector logic used in other CoT sector signals, ensuring consistency across positioning and flow overlays.

3. Per-Market Flow-Based Flags

Two key tactical flags are defined at the market level based on how flows interact with crowding:

trend\_confirm\_flow = (crowded\_long \wedge Flow\_4w > 0) \vee (crowded\_short \wedge Flow\_4w < 0)
contrarian\_flow = (crowded\_long \wedge Flow\_4w < 0) \vee (crowded\_short \wedge Flow\_4w > 0)

Trend-confirming flows reinforce existing crowded positions, while contrarian flows point to potential position unwinds or regime shifts.

4. Sector-Level Flow & Momentum

For each sector and report date, the model aggregates flow and positioning metrics:

Two additional derived metrics capture flow momentum and smoothing:

avg\_flow\_4w\_mom = \Delta\,avg\_flow\_4w \text{ (first difference by sector)}
avg\_flow\_4w\_3w\_ma = 3\text{-period rolling mean of } avg\_flow\_4w

Together, these form a sector-level view of whether flows are broadly supportive, deteriorating, or reversing.

5. Implementation (Python)

df_flow = df_cot_signals.copy()
df_flow["Report_Date"] = pd.to_datetime(df_flow["Report_Date"], errors="coerce")

# Ensure core columns
if "Flow_4w" not in df_flow.columns:
    raise KeyError("Expected column 'Flow_4w' not found in df_cot_signals.")

for flag in ["crowded_long", "crowded_short"]:
    if flag not in df_flow.columns:
        df_flow[flag] = False

# Numeric flow
df_flow["Flow_4w"] = pd.to_numeric(df_flow["Flow_4w"], errors="coerce")

# Sector classification
if "Sector" not in df_flow.columns:
    df_flow["Sector"] = df_flow.apply(_classify_sector_for_flow, axis=1)

# Per-market flow flags
flow = df_flow["Flow_4w"]

df_flow["trend_confirm_flow"] = (
    (df_flow["crowded_long"] & (flow > 0)) |
    (df_flow["crowded_short"] & (flow < 0))
)


df_flow["contrarian_flow"] = (
    (df_flow["crowded_long"] & (flow < 0)) |
    (df_flow["crowded_short"] & (flow > 0))
)

# Sector-level aggregation
group_cols = ["Report_Date", "Sector"]

df_sector_flow = (
    df_flow
    .groupby(group_cols)
    .agg(
        n_markets=("Market_and_Exchange_Names", "nunique"),
        avg_flow_4w=("Flow_4w", "mean"),
        pos_flow_share=("Flow_4w", lambda x: (x > 0).mean()),
        neg_flow_share=("Flow_4w", lambda x: (x < 0).mean()),
        trend_confirm_share=("trend_confirm_flow", "mean"),
        contrarian_flow_share=("contrarian_flow", "mean"),
        crowded_long_share=("crowded_long", "mean"),
        crowded_short_share=("crowded_short", "mean"),
    )
    .reset_index()
)

for col in [
    "pos_flow_share",
    "neg_flow_share",
    "trend_confirm_share",
    "contrarian_flow_share",
    "crowded_long_share",
    "crowded_short_share",
]:
    df_sector_flow[col] = df_sector_flow[col].astype(float)

# Sort and momentum
df_sector_flow = df_sector_flow.sort_values(["Sector", "Report_Date"]).reset_index(drop=True)


df_sector_flow["avg_flow_4w_mom"] = (
    df_sector_flow
    .groupby("Sector")["avg_flow_4w"]
    .diff()
)


df_sector_flow["avg_flow_4w_3w_ma"] = (
    df_sector_flow
    .groupby("Sector")["avg_flow_4w"]
    .transform(lambda s: s.rolling(3, min_periods=1).mean())
)

6. Interpretation

7. Limitations

8. Practical Use Cases