aether_quant.topology¶
The market topology overlay and its learned model's inputs.
The market topology overlay and its learned model's inputs.
MarketTopology
dataclass
¶
The market's correlation topology on one bar: nodes, links, clusters and the reasons for its state.
correlation_change_history
class-attribute
instance-attribute
¶
correlation_change_history: list[float] = field(default_factory=list)
TopologyCluster
dataclass
¶
One cluster of correlated names, with its average correlation.
TopologyLink
dataclass
¶
TopologyNode
dataclass
¶
One name's place in the topology: its cluster, 3D position, distance from the market and risk label.
apply_learned_topology ¶
apply_learned_topology(deterministic_topology: dict[str, Any], symbol_features: dict[str, dict] | None = None, previous_neighbors_by_symbol: dict[str, list[str]] | None = None, model: dict[str, Any] | None = None, feature_schema: dict[str, Any] | None = None, *, temperature: float = DEFAULT_TEMPERATURE, top_n_neighbors: int = DEFAULT_TOP_N_NEIGHBORS, min_confidence_for_learned: float = DEFAULT_MIN_CONFIDENCE_FOR_LEARNED, max_offset_xy: float = DEFAULT_MAX_OFFSET_XY, max_offset_z: float = DEFAULT_MAX_OFFSET_Z) -> dict[str, Any]
Overlay learned cluster probabilities/uncertainty/offsets on top of a deterministic MarketTopology.to_dict()-shaped payload.
Never raises. Falls back to the deterministic node unchanged (position
untouched, diagnostic fields zeroed) whenever the model is missing, a
symbol has no current-bar features, or the model's confidence for that
symbol is below min_confidence_for_learned.
build_market_topology ¶
build_market_topology(returns_by_symbol: dict[str, list[float]], regime_labels_by_symbol: dict[str, str] | None = None, correlation_threshold: float = 0.6, link_threshold: float = 0.5, min_observations: int = 5, embedding_iterations: int = 100, previous_positions: dict[str, tuple[float, ...]] | None = None, convergence_tolerance: float | None = None, top_peers_n: int = 3, previous_correlations: dict[tuple[str, str], float] | None = None, correlation_stability_tolerance: float | None = None, embedding_dimensions: int = 2, correlation_stability_tolerance_percentile: float | None = None, correlation_change_history: list[float] | None = None, correlation_change_history_max_len: int = 50, elevated_volatility_threshold: float = ELEVATED_VOLATILITY_THRESHOLD, elevated_volatility_threshold_by_symbol: dict[str, float] | None = None) -> MarketTopology
embedding_dimensions (V4-W3, phase_v2.topology.embedding_dimensions,
default 2) selects how many axes SMACOF embeds. At 2 - the default -
every coordinate, reason string and dimension is byte-identical to the
pre-V4 behavior, and z stays the volatility encoding. At 3, z becomes
a real correlation-distance axis on the same 0..100 scale as x/y, and
volatility is carried by node radius in the webui instead.
correlation_stability_tolerance_percentile (V4.9 Priority 1, default
None) is a self-relative alternative to the fixed
correlation_stability_tolerance above - development/Problems.md#36
found the fixed default poorly calibrated (the skip "essentially never
fires" against synthetic data, and this environment can't run a real
backtest to calibrate a better fixed number). When set, the effective
tolerance each bar is the given percentile of
correlation_change_history (a rolling window of RECENT PRIOR bars'
own max-pairwise-correlation-change values, NOT including this bar's -
avoids the circularity of a bar judging itself), making the skip rate
a mathematically-guaranteed function of the chosen percentile
regardless of the real correlation-noise distribution. Takes priority
over correlation_stability_tolerance when set; None (the default)
reproduces the fixed-tolerance behavior byte-identically. The caller
(main.py) is expected to thread correlation_change_history through
bar-to-bar exactly like previous_positions/previous_correlations -
read this call's returned MarketTopology.correlation_change_history
and pass it back in as next bar's correlation_change_history.
elevated_volatility_threshold (V5.2.6, development/Problems.md,
default matches the module's own ELEVATED_VOLATILITY_THRESHOLD
constant exactly - a no-op unless a caller passes a different value)
is the volatility_pressure cutoff for a node's topology_risk becoming
"elevated" - which analyzer/market_analyzer.py::build_market_analysis_decision()'s
Priority 3 uses to unconditionally override any directional signal.
Threaded through (not just a bare module constant) so main.py can
raise it to a value derived from this backtest universe's own real
historical volatility_pressure distribution, narrowing the override
to genuinely extreme readings instead of a fixed, ungrounded 0.45.
elevated_volatility_threshold_by_symbol (V5.5.0, Problems.md #129,
default None = every symbol uses the single threshold above) lets a
symbol carry its own cutoff - see resolve_elevated_volatility_thresholds().
Crypto's annualized volatility sits structurally above 0.45, so under
one global cutoff every crypto node was permanently "elevated" and the
analyzer's Priority 3 cancelled every BTC/LTC directional signal (13 of
its 26 book selections in the 2026-08-27 backtest).
learned_sizing_inputs ¶
learned_sizing_inputs(vector: dict[str, Any], model: dict[str, Any] | None, feature_schema: dict[str, Any] | None, *, cluster_regime_label: str | None = None, peer_distance: float = 0.5, temperature: float = DEFAULT_TEMPERATURE, min_confidence_for_learned: float = DEFAULT_MIN_CONFIDENCE_FOR_LEARNED) -> dict[str, Any]
The three numbers risk.position_sizing.topology_sizing_multiplier reads, for ONE node from its feature vector
alone: the same prototype distances, softmax confidence and regime-mismatch term _score_node computes. The peer
term (_jaccard_distance of the learned neighbours vs the deterministic cluster peers) needs the whole bar's
topology, so offline callers pass it as peer_distance (a bracket, 0.0..1.0). cluster_regime_label is the
deterministic cluster's dominant regime; None means "no mismatch information" (term 0). Never raises: a missing or
malformed model gives the fallback no-op.
liquidity_score_from_decision ¶
liquidity_score_from_decision(liquidity: dict[str, Any]) -> float
Maps a liquidity.market_liquidity.LiquidityDecision-shaped dict (or any dict carrying participation_rate/estimated_slippage) into a single 0..1 liquidity_score feature - higher is more liquid. Shared by train_topology.py (offline training) and main.py (runtime) so both sides compute this feature identically.
rank_correlated_peers ¶
rank_correlated_peers(symbol: str, eligible_symbols: list[str], correlation_fn: Callable[[str, str], float], top_n: int) -> list[str]
Ranks every OTHER eligible symbol by descending correlation to
symbol across the WHOLE eligible universe (not just symbol's own
cluster - a correlation_threshold-defined cluster can be much smaller
than top_n), returning up to top_n symbols. Ties broken
alphabetically for determinism (matches this module's existing
tie-breaking convention, e.g. sorted_roots/nodes.sort() below).
rank_correlated_peers_from_row ¶
rank_correlated_peers_from_row(row_index: int, symbols: list[str], correlation_row: ArrayLike, top_n: int, alphabetical_positions: ArrayLike | None = None) -> list[str]
rank_correlated_peers() on one row of the full correlation matrix (V5.8.0): the same ordering - descending
correlation, ties by symbol name - via one stable lexsort instead of a Python sort with a lookup per pair.
symbols must be in the matrix's row order; alphabetical_positions (rank of each symbol's name) defaults to the
row order, which equals alphabetical order for build_market_topology()'s sorted eligible symbols.
resolve_elevated_volatility_thresholds ¶
resolve_elevated_volatility_thresholds(raw_threshold: float | dict[str, Any] | None, asset_class_by_symbol: dict[str, str | None]) -> tuple[float, dict[str, float]]
V5.5.0 (Problems.md #129) - normalize phase_v2.topology.elevated_volatility_threshold.
Accepts the legacy bare float (one cutoff for everyone - unchanged behavior) or a dict like {"default": 0.45, "crypto": 1.2} keyed by asset class. Returns (default_threshold, {symbol: threshold}) where the per-symbol map only holds symbols whose class has its own entry. Malformed/non-finite/non-positive entries are ignored, never raised on.