Feature Catalog

All 179 features tested across the strategy exploration framework

Author
Affiliation

Rusty Conover

Query.Farm

Published

April 16, 2026

Show code
import sys, yaml
from collections import defaultdict
from IPython.display import Markdown
sys.path.insert(0, '/Users/rusty/Development/trading')
from farm_theme import apply as apply_farm_theme
apply_farm_theme()

with open('/Users/rusty/Development/trading/feature_library.yaml') as fh:
    features = yaml.safe_load(fh)

# Group by category
by_cat = defaultdict(list)
for f in features:
    by_cat[f['category']].append(f)

CATEGORY_ORDER = [
    'spread', 'correlation', 'currency', 'rates', 'credit', 'volatility',
    'precious_metals', 'energy', 'mining', 'equity', 'equity_factor',
    'financials', 'emerging', 'geographic', 'crypto', 'carry',
    'composite', 'raw_return', 'seasonal', 'calendar',
]

CATEGORY_DESCRIPTIONS = {
    'spread': 'Mean-reversion, momentum, and z-score features computed directly on the long-short spread.',
    'correlation': 'Rolling correlation between the two legs — captures regime transitions.',
    'currency': 'FX-based features (safe-haven flows, commodity currencies, dollar trend).',
    'rates': 'Treasury bond features capturing rate level, curve slope, and real-rate dynamics.',
    'credit': 'High-yield vs investment-grade credit-spread features.',
    'volatility': 'VIX-based and realized-volatility regime features.',
    'precious_metals': 'Gold and silver-based features (inflation/real-rate proxies, mining).',
    'energy': 'Oil-based features (commodity cycle, geopolitics).',
    'mining': 'Gold-mining and base-metal mining sector features.',
    'equity': 'Broad US equity market features (SPY, QQQ momentum and regime).',
    'equity_factor': 'Factor-rotation features (momentum, value, growth, low-vol, quality, beta).',
    'financials': 'Banking sector features (regional vs broad financials, credit positioning).',
    'emerging': 'Emerging-market equity features (EEM, EM breadth, EM vs DM).',
    'geographic': 'Geographic equity rotation (US vs intl, China vs EM).',
    'crypto': 'Bitcoin-based features as global liquidity / risk-on proxies.',
    'carry': 'Carry-trade and cross-asset positioning features.',
    'composite': 'Multi-asset composite features combining several underlyings.',
    'raw_return': 'Single-asset daily returns (used as direct features).',
    'seasonal': 'Calendar-based seasonality (day-of-week, month effects).',
    'calendar': 'Cyclical calendar encodings (sin/cos of day, month).',
}

print(f"Loaded {len(features)} features across {len(by_cat)} categories.")
Loaded 179 features across 20 categories.

Overview

This catalog documents every feature tested in the strategy exploration framework. Features are SQL fragments that reference daily returns ({ticker}_ret) and the spread ({long}_ret - {short}_ret) of the pair being analyzed. The exploration tool tests each feature individually against every candidate pair, then combines the top 3 features in a dual-model (LogisticRegression for direction + Ridge for magnitude) to produce trade signals.

Two placeholders are used:

  • {long} — replaced with the long ticker of the pair (e.g., xlf in the XLF/XLY pair)
  • {short} — replaced with the short ticker (e.g., xly)

Features are written for DuckDB SQL syntax. Most use window functions over an ordered time index t. A precomputed column cum_spread (the running cumulative log-ratio of the pair) is available for cointegration-style features.

Category Summary

Show code
import pandas as pd

rows = []
for cat in CATEGORY_ORDER:
    if cat in by_cat:
        rows.append({
            'Category': cat,
            'Count': len(by_cat[cat]),
            'Description': CATEGORY_DESCRIPTIONS.get(cat, ''),
        })
pd.DataFrame(rows).style.hide(axis='index')
Table 1
Category Count Description
spread 15 Mean-reversion, momentum, and z-score features computed directly on the long-short spread.
correlation 3 Rolling correlation between the two legs — captures regime transitions.
currency 15 FX-based features (safe-haven flows, commodity currencies, dollar trend).
rates 15 Treasury bond features capturing rate level, curve slope, and real-rate dynamics.
credit 4 High-yield vs investment-grade credit-spread features.
volatility 8 VIX-based and realized-volatility regime features.
precious_metals 5 Gold and silver-based features (inflation/real-rate proxies, mining).
energy 4 Oil-based features (commodity cycle, geopolitics).
mining 12 Gold-mining and base-metal mining sector features.
equity 9 Broad US equity market features (SPY, QQQ momentum and regime).
equity_factor 34 Factor-rotation features (momentum, value, growth, low-vol, quality, beta).
financials 5 Banking sector features (regional vs broad financials, credit positioning).
emerging 10 Emerging-market equity features (EEM, EM breadth, EM vs DM).
geographic 4 Geographic equity rotation (US vs intl, China vs EM).
crypto 3 Bitcoin-based features as global liquidity / risk-on proxies.
carry 4 Carry-trade and cross-asset positioning features.
composite 13 Multi-asset composite features combining several underlyings.
raw_return 8 Single-asset daily returns (used as direct features).
seasonal 2 Calendar-based seasonality (day-of-week, month effects).
calendar 6 Cyclical calendar encodings (sin/cos of day, month).

Feature Definitions

Show code
for cat in CATEGORY_ORDER:
    if cat not in by_cat:
        continue
    feats = by_cat[cat]
    print(f"\n### {cat.replace('_', ' ').title()} ({len(feats)} features)\n")
    print(f"*{CATEGORY_DESCRIPTIONS.get(cat, '')}*\n")
    for f in feats:
        requires = f.get('requires', [])
        req_str = ', '.join(requires) if requires else 'none'
        print(f"#### `{f['name']}`\n")
        print(f"{f['description']}\n")
        print(f"**Requires:** {req_str}\n")
        print("```sql")
        print(f["sql"])
        print("```\n")

# Catch any categories not in CATEGORY_ORDER
for cat, feats in by_cat.items():
    if cat in CATEGORY_ORDER:
        continue
    print(f"\n### {cat.replace('_', ' ').title()} ({len(feats)} features)\n")
    for f in feats:
        requires = f.get('requires', [])
        req_str = ', '.join(requires) if requires else 'none'
        print(f"#### `{f['name']}`\n")
        print(f"{f['description']}\n")
        print(f"**Requires:** {req_str}\n")
        print("```sql")
        print(f["sql"])
        print("```\n")

Spread (15 features)

Mean-reversion, momentum, and z-score features computed directly on the long-short spread.

spread_sma5

5d spread moving average

Requires: none

avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 5 PRECEDING AND 1 PRECEDING)

spread_sma20

20d spread moving average

Requires: none

avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

spread_vol20

20d spread vol (annualized)

Requires: none

stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) * sqrt(252)

spread_zscore

Spread z-score (mean reversion signal)

Requires: none

({long}_ret - {short}_ret - avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)) / nullif(stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING), 0)

spread_vol_regime

Spread vol regime (5d/60d)

Requires: none

stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 5 PRECEDING AND 1 PRECEDING) / nullif(stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING), 0)

spread_zscore60

60d spread z-score (medium mean reversion)

Requires: none

({long}_ret - {short}_ret - avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)) / nullif(stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING), 0)

spread_zscore100

100d spread z-score (slow mean reversion)

Requires: none

({long}_ret - {short}_ret - avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 100 PRECEDING AND 1 PRECEDING)) / nullif(stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 100 PRECEDING AND 1 PRECEDING), 0)

ratio_zscore60

60d cumulative-log-ratio z-score (cointegration-style mean reversion)

Requires: none

(cum_spread - avg(cum_spread) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)) / nullif(stddev(cum_spread) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING), 0)

ratio_zscore100

100d cumulative-log-ratio z-score (slow cointegration mean reversion)

Requires: none

(cum_spread - avg(cum_spread) OVER (ORDER BY t ROWS BETWEEN 100 PRECEDING AND 1 PRECEDING)) / nullif(stddev(cum_spread) OVER (ORDER BY t ROWS BETWEEN 100 PRECEDING AND 1 PRECEDING), 0)

cum_spread20

20d cumulative spread return (overshoot indicator)

Requires: none

sum({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

cum_spread60

60d cumulative spread return (overshoot indicator)

Requires: none

sum({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

spread_bollinger_pos

Bollinger band position (-1 lower, +1 upper)

Requires: none

({long}_ret - {short}_ret - avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)) / nullif(2.0 * stddev({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING), 0)

spread_acceleration

5d mean minus prior 15d mean (recent acceleration vs base)

Requires: none

(avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 5 PRECEDING AND 1 PRECEDING)) - (avg({long}_ret - {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 6 PRECEDING))

ratio_dist_sma60

Cumulative-log-ratio deviation from 60d mean (raw distance)

Requires: none

cum_spread - avg(cum_spread) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

ratio_dist_sma200

Cumulative-log-ratio deviation from 200d mean (long-term anchor)

Requires: none

cum_spread - avg(cum_spread) OVER (ORDER BY t ROWS BETWEEN 200 PRECEDING AND 1 PRECEDING)

Correlation (3 features)

Rolling correlation between the two legs — captures regime transitions.

corr20

20d pair correlation

Requires: none

corr({long}_ret, {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

corr60

60d pair correlation

Requires: none

corr({long}_ret, {short}_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

corr_delta

5d change in 20d correlation (regime transition)

Requires: none

corr({long}_ret, {short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) - corr({long}_ret, {short}_ret) OVER (ORDER BY t ROWS BETWEEN 25 PRECEDING AND 6 PRECEDING)

Currency (15 features)

FX-based features (safe-haven flows, commodity currencies, dollar trend).

safe_haven_fx

CHF+JPY combined safe-haven flow

Requires: fxf, fxy

(fxf_ret + fxy_ret) / 2.0

safe_haven_sma20

20d trend in safe-haven currency flows

Requires: fxf, fxy

avg((fxf_ret + fxy_ret) / 2.0) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

carry_trade

AUD/JPY carry trade proxy (risk-on/risk-off)

Requires: fxa, fxy

fxa_ret - fxy_ret

carry_sma20

20d carry trade trend

Requires: fxa, fxy

avg(fxa_ret - fxy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

dollar_mom20

20d USD momentum

Requires: uup

sum(uup_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

dollar_mom60

60d USD momentum

Requires: uup

sum(uup_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

chf_ret

CHF daily return

Requires: fxf

fxf_ret

jpy_ret

JPY daily return

Requires: fxy

fxy_ret

aud_ret

AUD daily return

Requires: fxa

fxa_ret

cad_ret

CAD daily return

Requires: fxc

fxc_ret

chf_mom20

CHF 20d momentum

Requires: fxf

sum(fxf_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

jpy_mom20

JPY 20d momentum

Requires: fxy

sum(fxy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

aud_mom20

AUD 20d momentum

Requires: fxa

sum(fxa_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

dollar_mom120

USD 120d momentum (long FX trend)

Requires: uup

sum(uup_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

dollar_mom200

USD 200d momentum (FX cycle)

Requires: uup

sum(uup_ret) OVER (ORDER BY t ROWS BETWEEN 200 PRECEDING AND 1 PRECEDING)

Rates (15 features)

Treasury bond features capturing rate level, curve slope, and real-rate dynamics.

tlt_ret

TLT daily return (long-end rate direction)

Requires: tlt

tlt_ret

tlt_mom20

TLT 20d momentum

Requires: tlt

sum(tlt_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

tlt_mom60

TLT 60d momentum (rate regime)

Requires: tlt

sum(tlt_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

curve_slope

Yield curve slope change (TLT - IEF)

Requires: tlt, ief

tlt_ret - ief_ret

curve_slope_sma20

20d yield curve trend

Requires: tlt, ief

avg(tlt_ret - ief_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

breakeven_chg

Breakeven inflation change (nominal - real rate)

Requires: tlt, tip

tlt_ret - tip_ret

breakeven_mom60

60d breakeven inflation momentum

Requires: tlt, tip

sum(tlt_ret - tip_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

real_rate_ret

TIPS return (real rate proxy)

Requires: tip

tip_ret

real_rate_mom20

20d real rate momentum

Requires: tip

sum(tip_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

tlt_mom120

TLT 120d momentum (long-rate trend)

Requires: tlt

sum(tlt_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

tlt_mom200

TLT 200d momentum (rate cycle)

Requires: tlt

sum(tlt_ret) OVER (ORDER BY t ROWS BETWEEN 200 PRECEDING AND 1 PRECEDING)

breakeven_mom120

120d breakeven inflation trend

Requires: tlt, tip

sum(tlt_ret - tip_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

breakeven_mom200

200d breakeven inflation trend (inflation cycle)

Requires: tlt, tip

sum(tlt_ret - tip_ret) OVER (ORDER BY t ROWS BETWEEN 200 PRECEDING AND 1 PRECEDING)

curve_mom60

60d yield-curve slope trend (long minus intermediate)

Requires: tlt, ief

sum(tlt_ret - ief_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

curve_mom120

120d yield-curve cycle

Requires: tlt, ief

sum(tlt_ret - ief_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

Credit (4 features)

High-yield vs investment-grade credit-spread features.

credit_spread

Credit spread change (HYG - IEF)

Requires: hyg, ief

hyg_ret - ief_ret

credit_sma20

20d credit spread trend

Requires: hyg, ief

avg(hyg_ret - ief_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

credit_mom60

60d credit-spread trend (HY vs IG)

Requires: hyg, lqd

sum(hyg_ret - lqd_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

credit_mom120

120d credit-spread cycle

Requires: hyg, lqd

sum(hyg_ret - lqd_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

Volatility (8 features)

VIX-based and realized-volatility regime features.

vix_level

VIX level

Requires: vix

vix_close

vix_deviation

VIX deviation from 20d mean

Requires: vix

vix_close - avg(vix_close) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

vix_ratio

VIX ratio to 60d mean

Requires: vix

vix_close / nullif(avg(vix_close) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING), 0)

long_vol_regime

Long-side vol regime (5d/60d)

Requires: none

stddev({long}_ret) OVER (ORDER BY t ROWS BETWEEN 5 PRECEDING AND 1 PRECEDING) / nullif(stddev({long}_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING), 0)

vol_ratio

Long/short vol ratio

Requires: none

stddev({long}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) / nullif(stddev({short}_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING), 0)

spy_vol20

SPY 20d realized vol (annualized)

Requires: spy

stddev(spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) * sqrt(252)

vix_mom60

VIX z-score vs 60d (vol regime)

Requires: vix

(vix_close - avg(vix_close) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)) / nullif(stddev(vix_close) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING), 0)

vix_mom120

VIX z-score vs 120d (slow vol cycle)

Requires: vix

(vix_close - avg(vix_close) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)) / nullif(stddev(vix_close) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING), 0)

Precious Metals (5 features)

Gold and silver-based features (inflation/real-rate proxies, mining).

gold_silver_spread

Gold vs silver return (safe-haven vs industrial)

Requires: gld, slv

gld_ret - slv_ret

gold_silver_sma20

20d gold-silver spread trend

Requires: gld, slv

avg(gld_ret - slv_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

gld_mom20

Gold 20d momentum

Requires: gld

sum(gld_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

gld_mom60

Gold 60d momentum

Requires: gld

sum(gld_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

gld_mom120

Gold 120d momentum (real rate cycle proxy)

Requires: gld

sum(gld_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

Energy (4 features)

Oil-based features (commodity cycle, geopolitics).

oil_ret

Oil daily return

Requires: uso

uso_ret

oil_mom20

Oil 20d momentum

Requires: uso

sum(uso_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

oil_mom60

Oil 60d momentum

Requires: uso

sum(uso_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

oil_mom120

Oil 120d momentum (commodity cycle)

Requires: uso

sum(uso_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

Mining (12 features)

Gold-mining and base-metal mining sector features.

cat_ret

Caterpillar daily return — mining equipment proxy

Requires: cat

cat_ret

cat_mom20

CAT 20d momentum — mining capex cycle

Requires: cat

sum(cat_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

cat_vs_spy

CAT relative to market — mining capex sentiment

Requires: cat, spy

cat_ret - spy_ret

cat_vs_spy_sma20

20d mining capex rotation trend

Requires: cat, spy

avg(cat_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

slx_ret

Steel ETF daily return

Requires: slx

slx_ret

slx_mom20

Steel 20d momentum — industrial cycle

Requires: slx

sum(slx_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

steel_vs_gold

Steel vs gold — industrial vs safe-haven metal

Requires: slx, gld

slx_ret - gld_ret

steel_vs_gold_sma20

20d industrial-vs-safe-haven metals trend

Requires: slx, gld

avg(slx_ret - gld_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

gold_oil_spread

Gold vs oil — safe-haven vs energy demand

Requires: gld, uso

gld_ret - uso_ret

gold_oil_sma20

20d gold-vs-oil trend

Requires: gld, uso

avg(gld_ret - uso_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

junior_senior_miners

Junior vs senior gold miners — mining risk appetite

Requires: gdxj, gdx

gdxj_ret - gdx_ret

junior_senior_sma20

20d junior-vs-senior miners trend

Requires: gdxj, gdx

avg(gdxj_ret - gdx_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

Equity (9 features)

Broad US equity market features (SPY, QQQ momentum and regime).

spy_ret

S&P 500 daily return

Requires: spy

spy_ret

spy_mom20

S&P 500 20d momentum

Requires: spy

sum(spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

spy_mom60

S&P 500 60d momentum

Requires: spy

sum(spy_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

eem_ret

Emerging markets daily return

Requires: eem

eem_ret

eem_mom20

EM 20d momentum

Requires: eem

sum(eem_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

em_vs_dm

EM vs DM equity spread

Requires: eem, spy

eem_ret - spy_ret

em_vs_dm_sma20

20d EM-DM trend

Requires: eem, spy

avg(eem_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

spy_mom120

S&P 500 120d momentum (equity bull/bear regime)

Requires: spy

sum(spy_ret) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

spy_mom200

S&P 500 200d momentum (cyclical regime)

Requires: spy

sum(spy_ret) OVER (ORDER BY t ROWS BETWEEN 200 PRECEDING AND 1 PRECEDING)

Equity Factor (34 features)

Factor-rotation features (momentum, value, growth, low-vol, quality, beta).

growth_value_spread

Growth (IWF) vs Value (IWD) daily return — style rotation

Requires: iwf, iwd

iwf_ret - iwd_ret

growth_value_sma20

20d trend in growth-vs-value rotation

Requires: iwf, iwd

avg(iwf_ret - iwd_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

growth_value_mom60

60d cumulative growth-vs-value rotation

Requires: iwf, iwd

sum(iwf_ret - iwd_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

small_large_spread

Small cap (IWM) vs Large cap (SPY) — size factor

Requires: iwm, spy

iwm_ret - spy_ret

small_large_sma20

20d size factor trend

Requires: iwm, spy

avg(iwm_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

tech_broad_spread

Tech (QQQ) vs broad (SPY) — tech leadership

Requires: qqq, spy

qqq_ret - spy_ret

tech_broad_sma20

20d tech-vs-broad trend

Requires: qqq, spy

avg(qqq_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

qqq_ret

Nasdaq-100 daily return

Requires: qqq

qqq_ret

qqq_mom20

Tech 20d momentum

Requires: qqq

sum(qqq_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

iwm_ret

Small cap daily return

Requires: iwm

iwm_ret

iwm_mom20

Small cap 20d momentum

Requires: iwm

sum(iwm_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

usmv_ret

Low-vol factor daily return

Requires: usmv

usmv_ret

low_vol_premium

Low-vol vs market — defensive equity premium

Requires: usmv, spy

usmv_ret - spy_ret

low_vol_premium_sma20

20d low-vol premium trend

Requires: usmv, spy

avg(usmv_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

mtum_ret

Momentum factor daily return

Requires: mtum

mtum_ret

momentum_premium

Momentum vs market — momentum factor premium

Requires: mtum, spy

mtum_ret - spy_ret

momentum_premium_sma20

20d momentum premium trend

Requires: mtum, spy

avg(mtum_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

mtum_mom60

Momentum factor 60d trend

Requires: mtum

sum(mtum_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

sphb_ret

High-beta daily return

Requires: sphb

sphb_ret

sphq_ret

Quality factor daily return

Requires: sphq

sphq_ret

high_beta_premium

High-beta vs market — risk-on indicator

Requires: sphb, spy

sphb_ret - spy_ret

quality_premium

Quality vs market — defensive equity premium

Requires: sphq, spy

sphq_ret - spy_ret

quality_vs_junk

Quality vs high-beta — flight to quality indicator

Requires: sphq, sphb

sphq_ret - sphb_ret

quality_vs_junk_sma20

20d quality-vs-junk trend (sustained risk-off)

Requires: sphq, sphb

avg(sphq_ret - sphb_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

quality_vs_junk_mom60

60d quality-vs-junk regime

Requires: sphq, sphb

sum(sphq_ret - sphb_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

vol_risk_premium

Low-vol vs high-beta — volatility risk premium

Requires: usmv, sphb

usmv_ret - sphb_ret

vol_risk_premium_sma20

20d volatility risk premium trend

Requires: usmv, sphb

avg(usmv_ret - sphb_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

momentum_vs_value

Momentum vs value — factor rotation

Requires: mtum, iwd

mtum_ret - iwd_ret

momentum_vs_value_sma20

20d momentum-vs-value rotation

Requires: mtum, iwd

avg(mtum_ret - iwd_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

momentum_vs_lowvol

Momentum vs low-vol — risk-on momentum vs defensive

Requires: mtum, usmv

mtum_ret - usmv_ret

iwn_ret

Small-cap value daily return

Requires: iwn

iwn_ret

small_value_premium

Small value vs small blend — value within small caps

Requires: iwn, iwm

iwn_ret - iwm_ret

small_value_vs_growth

Small value vs large growth — extreme style spread

Requires: iwn, iwf

iwn_ret - iwf_ret

small_value_vs_growth_sma20

20d small-value-vs-large-growth trend

Requires: iwn, iwf

avg(iwn_ret - iwf_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

Financials (5 features)

Banking sector features (regional vs broad financials, credit positioning).

bank_size_spread

Regional banks (KRE) vs broad financials (XLF) — bank stress indicator

Requires: kre, xlf

kre_ret - xlf_ret

bank_size_sma20

20d regional bank stress trend

Requires: kre, xlf

avg(kre_ret - xlf_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

xlf_ret

Financials sector daily return

Requires: xlf

xlf_ret

kre_ret

Regional banks daily return

Requires: kre

kre_ret

kre_mom20

Regional banks 20d momentum

Requires: kre

sum(kre_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

Emerging (10 features)

Emerging-market equity features (EEM, EM breadth, EM vs DM).

iemg_ret

Broad EM (IEMG) daily return

Requires: iemg

iemg_ret

fxi_ret

China large-cap daily return

Requires: fxi

fxi_ret

china_vs_em

China vs broad EM — China-specific risk

Requires: fxi, iemg

fxi_ret - iemg_ret

china_vs_em_sma20

20d China-vs-broad-EM trend

Requires: fxi, iemg

avg(fxi_ret - iemg_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

china_vs_us

China vs US — geopolitical/policy spread

Requires: fxi, spy

fxi_ret - spy_ret

china_vs_us_sma20

20d China-vs-US trend

Requires: fxi, spy

avg(fxi_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

em_breadth

IEMG vs EEM — small EM stocks (IEMG includes more small/mid)

Requires: iemg, eem

iemg_ret - eem_ret

iemg_mom20

Broad EM 20d momentum

Requires: iemg

sum(iemg_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

fxi_mom20

China 20d momentum

Requires: fxi

sum(fxi_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

fxi_mom60

China 60d momentum (commodity demand proxy)

Requires: fxi

sum(fxi_ret) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

Geographic (4 features)

Geographic equity rotation (US vs intl, China vs EM).

dollar_adjusted_long

Long-side return adjusted for USD moves

Requires: uup

{long}_ret + uup_ret

dollar_adjusted_sma20

20d dollar-adjusted long-side trend

Requires: uup

avg({long}_ret + uup_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

rate_differential

US rate curve slope (proxy for cross-border rate differential)

Requires: tlt, ief

tlt_ret - ief_ret

rate_diff_sma20

20d rate differential trend

Requires: tlt, ief

avg(tlt_ret - ief_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

Crypto (3 features)

Bitcoin-based features as global liquidity / risk-on proxies.

btc_ret

Bitcoin daily return — risk appetite proxy

Requires: btcusd

btcusd_ret

btc_mom20

Bitcoin 20d momentum

Requires: btcusd

sum(btcusd_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

btc_vol20

Bitcoin 20d realized vol

Requires: btcusd

stddev(btcusd_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) * sqrt(252)

Carry (4 features)

Carry-trade and cross-asset positioning features.

pff_ret

Preferred stocks daily return

Requires: pff

pff_ret

pff_vs_tlt

Preferreds vs Treasuries — yield-seeking spread

Requires: pff, tlt

pff_ret - tlt_ret

pff_vs_tlt_sma20

20d yield-seeking trend

Requires: pff, tlt

avg(pff_ret - tlt_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

pff_vs_hyg

Preferreds vs HY bonds — credit positioning

Requires: pff, hyg

pff_ret - hyg_ret

Composite (13 features)

Multi-asset composite features combining several underlyings.

risk_on_off

Risk-on (equities + credit) vs Risk-off (bonds + gold)

Requires: spy, eem, hyg, tlt, gld

(spy_ret + eem_ret + hyg_ret) / 3.0 - (tlt_ret + gld_ret) / 2.0

risk_on_off_sma20

20d risk-on/off trend

Requires: spy, eem, hyg, tlt, gld

avg((spy_ret + eem_ret + hyg_ret) / 3.0 - (tlt_ret + gld_ret) / 2.0) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

rate_credit_combo

Rates direction + credit spread combined

Requires: tlt, hyg, ief

tlt_ret + (hyg_ret - ief_ret)

defensive_basket

Defensive basket return — bonds, gold, safe-haven FX

Requires: tlt, gld, fxf, fxy

(tlt_ret + gld_ret + fxf_ret + fxy_ret) / 4.0

defensive_basket_sma20

20d defensive basket trend

Requires: tlt, gld, fxf, fxy

avg((tlt_ret + gld_ret + fxf_ret + fxy_ret) / 4.0) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

eem_vs_spy

Emerging vs developed equity rotation

Requires: eem, spy

eem_ret - spy_ret

eem_vs_spy_sma20

20d EM-vs-DM trend

Requires: eem, spy

avg(eem_ret - spy_ret) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

factor_basket_defensive

Defensive factor basket (low-vol + quality) vs market

Requires: usmv, sphq, spy

(usmv_ret + sphq_ret) / 2.0 - spy_ret

factor_basket_aggressive

Aggressive factor basket (momentum + high-beta) vs market

Requires: mtum, sphb, spy

(mtum_ret + sphb_ret) / 2.0 - spy_ret

factor_basket_spread

Defensive minus aggressive factors — risk regime

Requires: usmv, sphq, mtum, sphb

(usmv_ret + sphq_ret) / 2.0 - (mtum_ret + sphb_ret) / 2.0

factor_basket_spread_sma20

20d defensive-vs-aggressive factor regime trend

Requires: usmv, sphq, mtum, sphb

avg((usmv_ret + sphq_ret) / 2.0 - (mtum_ret + sphb_ret) / 2.0) OVER (ORDER BY t ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING)

factor_regime_mom60

60d defensive-vs-aggressive factor cycle

Requires: usmv, sphq, mtum, sphb

sum((usmv_ret + sphq_ret) / 2.0 - (mtum_ret + sphb_ret) / 2.0) OVER (ORDER BY t ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING)

factor_regime_mom120

120d defensive-vs-aggressive factor cycle

Requires: usmv, sphq, mtum, sphb

sum((usmv_ret + sphq_ret) / 2.0 - (mtum_ret + sphb_ret) / 2.0) OVER (ORDER BY t ROWS BETWEEN 120 PRECEDING AND 1 PRECEDING)

Raw Return (8 features)

Single-asset daily returns (used as direct features).

gld_ret

Gold daily return

Requires: gld

gld_ret

slv_ret

Silver daily return

Requires: slv

slv_ret

uso_ret

Oil daily return

Requires: uso

uso_ret

uup_ret

USD daily return

Requires: uup

uup_ret

hyg_ret

High yield bond daily return

Requires: hyg

hyg_ret

ief_ret

Mid-term Treasury daily return

Requires: ief

ief_ret

tip_ret

TIPS daily return

Requires: tip

tip_ret

xle_ret

Energy sector daily return

Requires: xle

xle_ret

Seasonal (2 features)

Calendar-based seasonality (day-of-week, month effects).

gold_season

Gold demand season (Oct-Nov, Jan-Feb)

Requires: none

CASE WHEN month(dt) IN (1, 2, 10, 11) THEN 1.0 ELSE 0.0 END

india_gold_season

Indian wedding/Diwali gold buying season

Requires: none

CASE WHEN month(dt) IN (10, 11, 12, 1, 2) THEN 1.0 ELSE 0.0 END

Calendar (6 features)

Cyclical calendar encodings (sin/cos of day, month).

dow_cos

Day of week cosine encoding

Requires: none

cos(2*pi()*dayofweek(dt)/5.0)

dow_sin

Day of week sine encoding

Requires: none

sin(2*pi()*dayofweek(dt)/5.0)

mon_fri

Monday/Friday flag

Requires: none

CASE WHEN dayofweek(dt) IN (1, 5) THEN 1.0 ELSE 0.0 END

month_end

Month-end flag (last 4 trading days)

Requires: none

CASE WHEN (last_day(dt) - dt)::INT <= 4 THEN 1.0 ELSE 0.0 END

month_cos

Month of year cosine encoding

Requires: none

cos(2*pi()*month(dt)/12.0)

month_sin

Month of year sine encoding

Requires: none

sin(2*pi()*month(dt)/12.0)

How Features Are Used

Each strategy in the portfolio selects 3 of these features after a per-pair feature scan. The scan tests every feature individually (just spread + feature) against the target pair and ranks by Sharpe ratio. The top 3 features are then combined in the dual-model architecture and tested across multiple holding periods (1, 2, 3, 5, 10 days) and minimum-move thresholds.


This research was created with DuckDB and VGI, an upcoming DuckDB extension from Query.Farm that allows custom aggregate functions to be written in any language with an Apache Arrow implementation.

Copyright © 2026 Query.Farm LLC. All rights reserved.