torrex

Torrex Basket Construction Methodology

**Chicago Global Quantitative Research**

1. Overview

Torrex constructs thematic equity baskets through unsupervised statistical discovery. Unlike traditional sector classifications (GICS, ICB) or imported factor models (Fama-French, Barra), baskets emerge from how stocks actually co-move within each market. The approach is deliberately bottom-up: let local market structure speak for itself, then label what it reveals.

The core pipeline runs in five stages:

  1. Data preparation – fetch returns, compute excess returns, build the correlation matrix
  2. Recursive coherence clustering – split the universe into groups that move together
  3. Naming – assign institutional-grade thematic labels via LLM reasoning
  4. Consolidation – merge over-fragmented baskets using a multi-signal hierarchy
  5. Scoring and rotation tracking – compute daily/weekly/monthly/YTD performance and classify momentum quadrants

Baskets are reformed quarterly (every 90 days) and scored daily. The initial production rollout covered fourteen markets. Coverage now spans 41 markets (see Section 2.3).


2. Universe Construction

2.1 Data Source

All price and volume data comes from LSEG Datastream (Refinitiv). The primary identifier is the Reuters Instrument Code (RIC). Stock metadata (country, listing status, name) comes from the same vendor’s reference data.

Only primary listings are included (DS_ISPRIMQT = 1), which eliminates duplicate cross-listings and depositary receipts.

2.2 Universe Filters

The live production universe uses a per-market market_filter plus two runtime gates defined in src/torrex/data_config.py and src/torrex/data_loader.py:

There is no live absolute USD market-cap, average-volume, or price floor. The current code uses a relative, currency-agnostic liquidity gate because legacy min_*_usd thresholds were compared against local-currency MKTCAP, which miscalibrated non-USD markets. The last China-only min_*_usd stubs were removed from UNIVERSE_FILTERS because the runtime never read them.

2.3 Market Selection Rationale

Telemetry covers 41 equity markets:

Region Markets
Americas United States, Canada, Mexico, Brazil, Chile
Europe United Kingdom, Germany, France, Netherlands, Belgium, Sweden, Norway, Denmark, Finland, Switzerland, Austria, Italy, Spain, Portugal, Poland, Hungary
Middle East and Africa Saudi Arabia, United Arab Emirates, Kuwait, Qatar, Israel, South Africa
Asia-Pacific Japan, China, Hong Kong, South Korea, Taiwan, Singapore, Malaysia, Thailand, Indonesia, Philippines, Vietnam, India, Australia, New Zealand

A market is included when it meets three criteria:

  1. LSEG Datastream coverage — LSEG must provide continuous OHLCV data with sufficient depth.
  2. Sufficient investable universe — Each market must have at least 10–15 stocks surviving the relative liquidity filter and 200-day history gate.
  3. Data history — At least 200 trading days of recent, high-quality price history.

The initial launch covered 14 markets. Japan, China, Hong Kong and Vietnam were added once vendor price coverage met these criteria. Russia is not covered, because LSEG coverage ended under sanctions. Turkey is not covered, because its historical price data has quality gaps.

2.3 Returns Computation

The clustering input is excess returns – individual stock returns minus the equal-weight market return on each day. Stripping broad market beta before clustering is critical: without it, the dominant cluster would always be “the market itself,” and all stocks would appear correlated simply because they share exposure to the same macro environment.

Returns are calculated as log returns: r_t = ln(P_t / P_{t-1}). Log returns compound additively (sum rather than product), which simplifies momentum calculations and avoids numerical overflow issues with long compounding chains. Period returns are recovered via np.expm1(sum(log_returns)).

The clustering lookback window is 504 trading days (approximately two years). This is long enough to capture secular co-movement patterns while remaining responsive to structural shifts in market relationships.

2.4 Correlation Matrix

The pairwise correlation matrix is the foundation of all clustering decisions. Two methodological choices are worth noting:

Exponentially weighted correlations. By default, the system uses EWM correlations with a 126-day half-life (approximately six months). Recent co-movement receives higher weight than distant history. This reflects the reality that market structure evolves – a basket of “China export chain” stocks may behave very differently after a trade policy shift than before it.

Signed distance metric. Correlation is converted to distance via d = (1 - rho) / 2, mapping the range [-1, +1] to [1, 0]. This preserves the information that negatively correlated stocks are maximally dissimilar. The alternative (1 - |rho|) would treat strong negative correlation as proximity, which would group natural hedges together – useful for pairs trading, but wrong for rotation analysis.


3. Recursive Coherence Clustering

3.1 Philosophy

Standard k-means or flat hierarchical clustering requires specifying the number of clusters upfront. This is problematic across heterogeneous markets: the Korean market may have 25 natural groupings while Saudi Arabia has 12. Forcing a fixed k onto different markets either over-fragments small markets or under-segments large ones.

Torrex solves this with recursive coherence clustering: start with the full universe as one cluster, test whether it has statistically significant internal structure, and if so, split it. Repeat on each child until every cluster is either (a) internally coherent or (b) too small to split further.

The algorithm discovers the number of baskets as an output, not an input.

3.2 Algorithm

The recursive procedure is as follows:

function SPLIT(tickers, depth):
    1. If |tickers| < 2 * min_cluster_size:
         STOP (too small to split)

    2. If |tickers| > max_cluster_size:
         FORCE SPLIT (oversized, even if coherent)

    3. Run coherence test on tickers:
         a. Compute mean pairwise correlation (internal_corr)
         b. Bootstrap: draw 200-1000 random samples of same size
            from the full universe, compute their internal_corr
         c. Percentile = fraction of random samples below internal_corr
         d. PASS if percentile >= threshold AND internal_corr >= absolute_threshold

    4. If PASS:
         STOP (cluster is coherent -- its members move together
               more than random chance would predict)

    5. If internal_corr >= weak_gate (0.12):
         STOP (weak but non-random structure -- splitting further
               would create noise)

    6. If depth >= max_depth:
         STOP (recursion limit)

    7. SPLIT:
         a. Find optimal k via silhouette score on the sub-universe
         b. Apply agglomerative clustering (average linkage)
         c. Absorb any child with < min_cluster_size stocks
            into its nearest sibling (by cross-correlation)
         d. Absorb any child with internal_corr < min_internal_corr
         e. Guard against degenerate peeling: if largest child >= 90%
            of parent, fall back to KMeans
         f. Recurse on each surviving child

3.3 Coherence Test

The coherence test is the key statistical gatekeeping mechanism. It answers: “Is this group of stocks more correlated internally than you would expect from a random selection of the same size?”

The test has two prongs, and a cluster must pass both:

  1. Bootstrap test. Draw random groups of the same size from the full universe, compute their mean pairwise correlation, and rank the cluster’s correlation against this distribution. The cluster must rank at or above the 75th percentile. This controls for the mechanical relationship between group size and average correlation (larger groups tend toward the market mean).

  2. Absolute correlation threshold. The cluster’s internal correlation must exceed 0.15 (configurable per market). This prevents accepting clusters that beat random samples but still have negligible co-movement in absolute terms.

The bootstrap uses adaptive sampling: 200 initial samples for a quick read, escalating to 1000 if the result is borderline (within 15 percentage points of the threshold). This balances computational cost against precision for marginal cases.

3.4 Configuration Parameters

Parameter Default Description
min_cluster_size 10 (8-15 by market) Minimum viable basket size
coherence_percentile 75 Bootstrap percentile threshold
absolute_corr_threshold 0.15 (0.10 for SA) Minimum absolute correlation
min_internal_corr 0.08 Sub-cluster absorption threshold
max_cluster_size 100 Force split above this
max_depth 5 (7 for SA) Recursion depth limit

Saudi Arabia uses a lower absolute correlation threshold (0.10) and deeper recursion (max_depth=7) because its smaller, less liquid universe produces noisier correlation estimates. The US uses a larger minimum cluster size (15) to avoid fragmentation in a deep market.

3.5 Post-Split Enforcement

After each split, two enforcement rules prevent pathological fragmentation:

Size enforcement. Any sub-cluster smaller than min_cluster_size is merged into its nearest sibling, where “nearest” is defined by the highest average cross-correlation between the sub-cluster and each sibling.

Coherence enforcement. Any sub-cluster with internal correlation below min_internal_corr (0.08) is similarly absorbed. This catches cases where a split produces one strong cluster and one incoherent residual.

Degenerate split detection. If agglomerative clustering produces a largest child containing 90% or more of the parent, the algorithm falls back to KMeans on distance features. This addresses the “peeling” pathology where average-linkage hierarchical clustering iteratively detaches small outlier groups rather than finding balanced partitions.


4. Basket Naming

4.1 Design Principle

Names should describe the investment thesis, not the GICS sector. The question is: “What bet is a portfolio manager making by owning this basket?” This yields names like “Semiconductor Equipment,” “Rate-Sensitive REITs,” or “China Export Chain” rather than “Information Technology” or “Real Estate.”

4.2 Naming Pipeline

Each cluster is enriched with three types of context before naming:

  1. Member stock identities. Company names and RICs (up to 10 per cluster).
  2. Business descriptions. Short descriptions of what each company does, sourced from LSEG’s TRBC business descriptions (up to 150 characters each).
  3. GICS sector composition. The percentage breakdown of industry classifications within the cluster (top 5 sectors).

This context is passed to a reasoning LLM (Z.AI GLM-4-Plus via cloud API, with Ollama qwen3:14b as local fallback) with a structured prompt that enforces a classification priority:

  1. Business line first. What do these companies actually do? Name the specific business model.
  2. Macro exposure second. If business lines are mixed, what macro factor ties them together?
  3. Market segment third. If neither applies, describe the investment profile (size, style, ownership structure).

The prompt includes explicit examples of good names (specific, investor language, 2-5 words) and bad names (generic sector labels, GICS echoes), along with hard constraints against names ending in generic suffixes like “Services” or “Products.”

4.3 Quality Controls


5. Basket Consolidation

5.1 Motivation

Recursive clustering occasionally over-fragments. Two clusters named “Hospital Operators” and “Healthcare Providers” may represent the same rotation theme that the algorithm split because of one noisy sub-group. Consolidation detects and merges such duplicates.

This is a separate pass from clustering, not an inline correction, because it operates on different signals: return correlations between baskets (not between individual stocks), business description similarity, and thematic naming overlap.

5.2 Three-Stage Process

Stage 0: Absorb tiny baskets. Any basket with fewer than min_basket_size stocks (default: 5) is merged into its nearest viable neighbor. “Nearest” is determined by return correlation if available, with description embedding similarity as fallback.

Stage 1: Exact-name matches. Baskets with identical normalized names (case-insensitive, punctuation-stripped) are grouped deterministically. No model is needed for this – it catches the straightforward duplicates.

Stage 2: Multi-signal merge candidates. For the remaining baskets, a three-signal hierarchy identifies merge candidates:

Signal Role Source
Return correlation Primary Computed from member stock returns (no cold-start problem)
Description embedding similarity Secondary confirmation nomic-embed-text embeddings of business descriptions
Name similarity Pre-filter only Word-Jaccard on normalized names

The decision logic proceeds as follows:

5.3 LLM Veto (Not Confirm)

The default for candidates that pass the signal hierarchy is merge. The LLM’s role is to veto bad merges, not to confirm good ones. This inverted default is deliberate: the quantitative signals (return correlation + embedding similarity) are the primary evidence, and the LLM acts as a qualitative safety net for cases where numbers mislead.

The veto model (qwen3:8b, local Ollama) receives the basket names, sector compositions, and member companies, and is asked: “Should these baskets NOT be merged?” Only an explicit veto blocks the merge.

5.4 Post-Consolidation

Merged baskets receive new Layer 2 names via a renaming pass (qwen3:14b). The combined member list provides richer context for naming, often producing better labels than the initial per-cluster naming.

Sub-baskets are retired in the registry. The consolidated super-basket inherits all member stocks (union, with deduplication). A cap of max_combined_size (default: 50) and max_group_size (default: 8 baskets merged into one) prevents runaway consolidation.

5.5 Rolling Stability

Return correlation alone can be misleading if two baskets were highly correlated during one regime but uncorrelated otherwise. The system computes rolling stability: the fraction of 63-day (3-month) rolling windows where pairwise correlation exceeds 0.50.

High stability + high correlation = structural relationship (merge). High correlation + low stability = regime-driven spurious correlation (block).

The stability requirement for auto-merge is 0.60, meaning the pair must have been correlated in at least 60% of rolling windows.


6. Scoring and Rotation Tracking

6.1 Performance Metrics

Active baskets are scored daily with four time horizons:

Metric Window Calculation
Daily 1 trading day Equal-weight mean of member log returns, converted via expm1
WTD Week-to-date Compounded from Monday open
MTD Month-to-date Compounded from first trading day of month
YTD Year-to-date Compounded from first trading day of year

All returns are equal-weight across basket members. This is intentional: market-cap weighting would concentrate baskets around their largest member, obscuring the rotation signal. A 10-stock basket where the top name is 50% of cap-weight is really a single-stock bet. Equal-weight ensures the basket reflects the theme, not one dominant name.

6.2 Factor Scores

Each basket carries median factor exposures across its members, computed from the latest cross-sectional factor model scores:

These factor medians contextualize basket performance. A basket in the “Leaders” quadrant with high momentum scores and low value scores tells a different story than one with the reverse profile.

6.3 Rotation Map

The rotation map is a two-axis scatter plot that classifies each basket into one of four quadrants:

The four quadrants:

Quadrant Long-Term Short-Term Interpretation
Leaders Positive Positive Strong and accelerating
Fading Positive Negative Was strong, now weakening
Recovering Negative Positive Was weak, now strengthening
Laggards Negative Negative Weak and decelerating

The key insight is in migrations between quadrants, not static positions. A basket moving from Leaders to Fading signals that the theme is losing momentum – a potential rotation out. A basket moving from Laggards to Recovering may be the next leadership theme.

6.4 Anomaly Signals

The system generates up to four automated signals per reporting date:

  1. Biggest daily move. The basket with the largest absolute daily return.
  2. Z-score outlier. Any basket with a cross-sectional daily return z-score exceeding 2.0.
  3. Market-level extreme. Any market whose average basket return exceeds 0.5% (flagging broad market dislocations).
  4. Trend divergence. Any basket where the daily return direction contradicts the MTD direction and the daily move exceeds 2% (potential reversal signal).

7. Quarterly Reformation

7.1 Cycle

Baskets are reformed every 90 calendar days, aligned with corporate reporting cycles and policy shifts. The system auto-detects when reformation is due based on the most recent formation date in the registry.

7.2 Reformation Pipeline

For each market, the full pipeline runs:

  1. Data fetch. Pull the latest 504 days of returns (via a local cache).
  2. Clustering. Run recursive coherence clustering on the current universe.
  3. Enrichment. Fetch business descriptions and GICS data for all member stocks (one data request per market).
  4. Naming. Assign thematic labels to each new basket.
  5. Turnover measurement. Compare new compositions against prior quarter’s baskets.
  6. Registry update. Retire old baskets, register new ones in the parquet DB.
  7. Consolidation. Merge over-fragmented baskets.
  8. Score update. Compute performance metrics for all active baskets.
  9. Dashboard regeneration. Write updated JSON for the static frontend.

7.3 Continuity Across Reformations

Old baskets are retired (status changed to “retired” with a retirement date) rather than deleted. The registry is an append-only ledger, preserving full basket history. This allows historical analysis of how thematic structure evolves over time – which themes persist, which dissolve, and which emerge.


8. Data Architecture

8.1 Local Storage (Parquet + DuckDB)

All state is stored in parquet files, queried via DuckDB (in-process, no database server). The key tables:

Table Pattern Description
registry.parquet Append-only Basket metadata (ID, market, name, status, dates)
compositions.parquet Append-only Basket-to-ticker mappings
daily_summaries.parquet Append-only Daily performance snapshots
basket_returns.parquet Append-only Basket-level daily returns
baskets.parquet Rebuild from registry Flat ledger for quick access
basket_scores.parquet Overwritten daily Latest performance + factor scores

8.2 Upstream Data

Price data (LSEG Datastream), security metadata, Parallax factor scores and business descriptions come from our central data warehouse. Data is fetched and cached locally; the system does not depend on continuous warehouse connectivity for daily operations.


9. Design Rationale

9.1 Why Not Standard Factor Models?

Fama-French, Barra, and similar frameworks were developed for and calibrated on US large-cap equities. Applying them directly to Malaysian mid-caps or Saudi financials introduces model misspecification that is difficult to detect. The factor loadings, the risk premia, and even the relevant factors differ across markets.

Torrex takes the opposite approach: discover local structure first, then ask what it resembles. A cluster of Malaysian stocks that move together may correspond to “palm oil plantation operators” – a theme that no standard factor model would capture because it is specific to markets with significant commodity-linked equity exposure.

9.2 Why Equal-Weight Baskets?

Equal-weight construction ensures baskets reflect thematic co-movement rather than the performance of one dominant name. In a cap-weight “Korean Semiconductor” basket, Samsung would dominate, and the basket would track Samsung plus noise. Equal-weight forces each member to contribute equally, revealing whether the theme is in rotation or just one mega-cap.

9.3 Why Correlation-Based Distance (Not Returns-Based)?

Clustering on raw returns would conflate level effects with co-movement. A stock with 50% annual returns and a stock with -20% returns can have correlation of 0.90 – they move together, just at different levels. Correlation captures the co-movement pattern, which is what rotation analysis requires.

9.4 Why Recursive Splitting (Not Flat Clustering)?

Flat clustering (k-means, fixed-k hierarchical) assumes the market has a single level of structure. In practice, markets have nested structure: “Financials” contains “Banks” which contains “Islamic Banking” which contains “Malaysian Islamic Banks.” Recursive splitting reveals this hierarchy and stops at the appropriate granularity for each branch, rather than forcing every branch to the same depth.

9.5 Why LLM Naming (Not Rule-Based)?

Rule-based naming from GICS sectors would produce labels like “Information Technology” for a cluster containing a mix of semiconductor equipment makers, data center REITs, and power infrastructure companies. The unifying theme is “AI Infrastructure Buildout” – a thesis-level label that requires understanding what these businesses share beyond their GICS codes. LLMs with access to business descriptions can identify these cross-sector themes.

9.6 Why Consolidation as a Separate Pass?

Clustering operates on individual stock correlations. Consolidation operates on basket-level return correlations, which are cleaner (diversification averages out idiosyncratic noise) and more directly relevant to the question “are these two baskets the same rotation theme?” Separating the two stages allows each to use its most appropriate signal.


10. Limitations and Known Issues

  1. Correlation is not causation. Two baskets may be highly correlated because they share an unobserved macro exposure, not because they represent the same theme. The description embedding safety valve mitigates this but does not eliminate it.

  2. Equal-weight assumption. Equal-weight returns do not reflect achievable portfolio returns for large allocators. Market impact and liquidity constraints in frontier markets may make some baskets uninvestable at institutional scale.

  3. Quarterly reformation lag. The 90-day reformation cycle means baskets can become stale if market structure shifts abruptly (e.g., a sudden policy change or sector shock). Daily scoring partially compensates, but membership remains fixed between reformations.

  4. Naming model dependency. Basket names depend on LLM quality and prompt engineering. Different models or prompt versions may produce different names for the same cluster. Names should be treated as informative labels, not stable identifiers; the basket_id is the stable key.

  5. Survivorship in the universe. The universe is constructed from currently-listed stocks with sufficient history. Stocks that delisted during the lookback window are excluded, introducing mild survivorship bias in the correlation estimates.

  6. Single correlation regime. EWM weighting addresses non-stationarity partially, but the system does not explicitly model regime switches. A cluster that was coherent in a bull market may fragment in a crisis, and vice versa.


Document version: 2026-06-09. Launch-cohort rationale retained; live universe rules synced to the current production code.