This document is the replacement documentation for the Regime Detection Module in the fin-glassbox project:
An Explainable Multimodal Neural Framework for Financial Risk Management
The Risk Engine entrypoint is implemented in:
code/riskEngine/regime_gnn.py
The actual graph-learning implementation is kept in:
code/gnn/mtgnn_regime.py
regime_gnn.py is intentionally a thin wrapper. It exposes a Risk Engine-facing command while preserving the graph implementation inside code/gnn/, where graph models belong.
The Regime Detection Module identifies the current broad market state. It acts as a bridge between market behaviour, macro stress, and graph structure.
Temporal Encoder embeddings
│
FinBERT / text embeddings where available
│
FRED macro/regime features
│
Cross-asset graph snapshots
│
▼
MTGNN Regime Detection
├── graph building / adjacency learning
├── graph property extraction
├── macro stress features
└── regime classifier
│
▼
outputs/results/MTGNNRegime/predictions_chunk{chunk}_{split}.csv
│
▼
Position Sizing Engine → Quantitative Analyst → Fusion Engine
The module does not forecast a specific stock’s price. It classifies the market environment so downstream risk logic can behave differently in calm, volatile, crisis, or rotation states.
A market regime is a behavioural state of the market. Examples:
| Regime | Meaning |
|---|---|
| calm | low stress, weaker systemic risk, more stable conditions |
| volatile | unstable movement, increased uncertainty |
| crisis | broad stress, high correlation, high risk of systemic drawdowns |
| rotation | sector/cluster transition where capital moves between groups |
The same stock signal should be interpreted differently across regimes. A moderate BUY-like signal in a calm market may be acceptable. The same signal during a crisis may need a much smaller position or may be rejected by the Fusion rule barrier.
NLP/text explains why market conditions may be changing. Regime detection captures what the market is actually doing structurally.
This module is mandatory because:
The Regime Detection Module therefore provides context for risk interpretation.
The file:
code/riskEngine/regime_gnn.py
imports and re-exports the implementation from:
code/gnn/mtgnn_regime.py
The wrapper imports:
MTGNNRegimeConfig
MTGNNRegimeModel
RegimeSnapshotDataset
build_graph_summary
run_hpo
train_regime_model
predict_with_xai
cmd_inspect
cmd_smoke
main
This means the Risk Engine can call the regime module as:
python code/riskEngine/regime_gnn.py predict --repo-root . --chunk 1 --split test --device cuda --node-limit 2500
while the actual graph model remains documented and maintained in the GNN folder.
The module can use several data families.
outputs/embeddings/TemporalEncoder/chunk{chunk}_{split}_embeddings.npy
outputs/embeddings/TemporalEncoder/chunk{chunk}_{split}_manifest.csv
These provide per-ticker market behaviour representations.
outputs/embeddings/FinBERT/chunk{chunk}_{split}_embeddings.npy
outputs/embeddings/FinBERT/chunk{chunk}_{split}_metadata.csv
Text embeddings are useful for macro/event context when they can be aligned.
data/FRED_data/outputs/macro_features_trading_days_clean.csv
Key macro features include:
yield_spread_10y2y
yield_spread_10y3m
credit_spread_baa_aaa
regime_yield_inverted
DFF
DGS10
DGS2
DGS3MO
BAA10Y
AAA10Y
These features give the regime model explicit macro stress context.
data/graphs/snapshots/edges_YYYY-MM-DD.csv
Each snapshot contains edges such as:
window_start
source
target
correlation
These snapshots encode cross-asset co-movement and dependency structure.
The implementation uses an MTGNN-inspired graph builder and classifier. In this project, MTGNN is not used as a full general-purpose forecasting engine. Its role here is narrower:
learn / summarise graph structure → extract graph stress properties → classify regime
This limited usage is intentional and should be defended as follows:
The regime model therefore respects the project boundary that GNNs live in graph/risk modules rather than replacing the technical encoder.
The model outputs regime classes such as:
calm
volatile
crisis
rotation
The module can use rule-derived labels when no human-labelled regime dataset exists. Label construction uses graph and macro stress logic such as:
high graph density + high stress → crisis
moderate density/stress → volatile
low stress + stable graph → calm
sector/cluster changes → rotation
This is acceptable because the model is learning a defensible internal risk-state policy rather than claiming access to external proprietary regime labels.
Prediction outputs are written to:
outputs/results/MTGNNRegime/predictions_chunk{chunk}_{split}.csv
Important columns include:
| Column | Meaning |
|---|---|
ticker or snapshot key |
date/snapshot identification depending on output granularity |
date |
regime date |
regime_id |
numeric predicted regime |
regime_label |
calm / volatile / crisis / rotation |
regime_confidence |
confidence in predicted regime |
prob_calm |
probability of calm state |
prob_volatile |
probability of volatile state |
prob_crisis |
probability of crisis state |
prob_rotation |
probability of rotation state |
regime_transition_probability |
estimated probability of state transition |
graph_density |
graph connectedness / stress proxy |
avg_degree_norm |
normalised average graph degree |
std_degree_norm |
dispersion in graph degree |
mean_edge_weight |
mean edge strength |
max_edge_weight |
strongest edge weight |
graph_entropy |
graph dispersion/uncertainty measure |
learned_graph_stress |
learned graph stress score |
macro_stress_score |
macro-derived stress estimate |
label_graph_stress_score |
graph stress used in label construction/audit |
These outputs are merged into Position Sizing and Quantitative Analyst.
python code/riskEngine/regime_gnn.py inspect --repo-root .
python code/riskEngine/regime_gnn.py smoke --repo-root . --device cuda
python code/riskEngine/regime_gnn.py hpo --repo-root . --chunk 1 --trials 30 --device cuda --fresh --node-limit 2500
python code/riskEngine/regime_gnn.py train-best --repo-root . --chunk 1 --device cuda --fresh --node-limit 2500
python code/riskEngine/regime_gnn.py predict --repo-root . --chunk 1 --split val --device cuda --node-limit 2500
python code/riskEngine/regime_gnn.py predict --repo-root . --chunk 1 --split test --device cuda --node-limit 2500
python code/riskEngine/regime_gnn.py train-best --repo-root . --chunk 1 --device cuda --fresh --node-limit 2500 && python code/riskEngine/regime_gnn.py predict --repo-root . --chunk 1 --split val --device cuda --node-limit 2500 && python code/riskEngine/regime_gnn.py predict --repo-root . --chunk 1 --split test --device cuda --node-limit 2500
Regime XAI is graph- and macro-based. It should explain both predicted state and state drivers.
XAI output is written under:
outputs/results/MTGNNRegime/xai/
The module’s explanation levels include:
The model reports graph properties such as:
These explain why the model saw a calm, volatile, crisis, or rotation state.
A useful regime explanation is not only “what is the graph now?” but also:
what changed from the previous period?
This can include changes in graph density, edge concentration, transition probability, and macro stress.
The regime module may include GNNExplainer-style or approximation-based edge explanations to identify which relationships contributed most to the regime classification.
Position Sizing uses regime information for two purposes:
The default Position Sizing weight for regime risk is:
regime weight = 0.10
Regime risk also triggers hard caps:
volatile regime cap = 6%
rotation regime cap = 5%
crisis short-horizon cap = 5%
crisis long-horizon cap = 3%
This makes regime a final risk-control context rather than a mere classifier.
A healthy regime run should show:
A validation accuracy around a simple majority baseline is not automatically failure because regime labels are rule-derived and often imbalanced. The more important requirement is that the module generates stable, finite, explainable market-state context.
The regime module can be memory-heavy with large node limits. If CUDA memory is under pressure, reduce --node-limit or run after other GPU jobs finish.
Earlier runs showed CUDA out-of-memory when another GPU process was active. That is not necessarily a model failure; it can be resource contention.
The Regime Detection Module has these limitations:
These limitations are controlled by using regime as a contextual risk signal rather than the only decision source.
The Regime Detection Module is a Risk Engine component exposed through code/riskEngine/regime_gnn.py and implemented through code/gnn/mtgnn_regime.py. It supplies market-state context to Position Sizing, Quantitative Analyst, Fusion, and final trade approval.