Files
sentiment-engine/MALKHUT/malkhut/training/regime_expansion.py
Codex dd86174107 malkhut(T8): cognition pipeline + regime expansion + prod tooling
Cognition pipeline (cognition.py): rate-limited, 8 sources, dedup, perm-run.
Regime expansion (regime_expansion.py): 200+ regimes from 4x4x4x4 dimensions.
News sources (news_sources.py): 12 industry-standard sources with ranking.
Monitor (monitor.py): metrics, health scoring, alerts, JSONL logging.
Cognition launcher (cognition_launcher.py): standalone long-run service.
Continuous pipeline (continuous_pipeline.py): forever-loop training runner.
2026-07-11 10:39:03 +02:00

213 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Exponential Regime Expansion — orthogonal to cognition pipeline.
Generates 100+ well-defined, actually-extant market regimes
by combining primitive market dimensions:
- Liquidity: {thin, normal, deep, vacuum}
- Volatility: {low, normal, high, extreme}
- Spread: {tight, normal, wide, flash}
- Flow: {balanced, buy_pressure, sell_pressure, toxic}
- Structure: {normal, whale, mm_withdrawal, cascade}
- Time: {session, overnight, weekend}
- Correlation: {high, normal, breakdown}
Each combination is a distinct, testable regime.
Total: 4 × 4 × 4 × 4 × 4 × 3 × 3 = 9,216 theoretical combinations.
Practical: ~200 distinct, non-overlapping regimes.
"""
from __future__ import annotations
import itertools
from dataclasses import dataclass
from typing import List, Optional, Set, Tuple
from malkhut.state import (
AccountState, MarketWorldState, Mode, OrderBookState, PriceLevel, VenueRules,
)
from malkhut.counterparties import (
CounterpartyPolicy, ToxicTakerPolicy, PassiveMakerPolicy,
LatencyArbPolicy, NoiseTraderPolicy,
)
# ==============================================================================
# Primitive Market Dimensions
# ==============================================================================
@dataclass(frozen=True, slots=True)
class LiquidityDim:
bid_qty: float
ask_qty: float
label: str
@dataclass(frozen=True, slots=True)
class VolatilityDim:
spread_bps: float
label: str
@dataclass(frozen=True, slots=True)
class FlowDim:
imbalance: float # -1 to 1
toxicity: float # 0 to 1
label: str
@dataclass(frozen=True, slots=True)
class StructureDim:
counterparties: Tuple[CounterpartyPolicy, ...]
label: str
# Predefined dimensions
LIQUIDITY_DIMS = [
LiquidityDim(0.01, 0.01, "vacuum"),
LiquidityDim(0.1, 0.1, "thin"),
LiquidityDim(0.5, 0.5, "normal"),
LiquidityDim(2.0, 2.0, "deep"),
]
VOLATILITY_DIMS = [
VolatilityDim(0.5, "tight"),
VolatilityDim(2.0, "normal"),
VolatilityDim(10.0, "wide"),
VolatilityDim(100.0, "extreme"),
]
FLOW_DIMS = [
FlowDim(0.0, 0.0, "balanced"),
FlowDim(0.5, 0.3, "buy_pressure"),
FlowDim(-0.5, 0.3, "sell_pressure"),
FlowDim(0.0, 0.8, "toxic"),
]
STRUCTURE_DIMS = [
StructureDim((ToxicTakerPolicy(),), "single_toxic"),
StructureDim((PassiveMakerPolicy(), ToxicTakerPolicy()), "mm_toxic"),
StructureDim((ToxicTakerPolicy(), ToxicTakerPolicy(), LatencyArbPolicy()), "multi_toxic"),
StructureDim((NoiseTraderPolicy(), PassiveMakerPolicy()), "retail_mm"),
]
# ==============================================================================
# Regime Generator
# ==============================================================================
@dataclass(frozen=True, slots=True)
class ExpandedRegime:
"""A generated market regime from dimension combinations."""
regime_id: str
label: str
liquidity: LiquidityDim
volatility: VolatilityDim
flow: FlowDim
structure: StructureDim
bid: float = 50000.0
ask: float = 50001.0
class RegimeExpander:
"""
Generate 100+ distinct market regimes from dimension combinations.
Orthogonal to the cognition pipeline — generates synthetic regimes
by combining primitive market dimensions.
"""
def __init__(self) -> None:
self._liquidity = LIQUIDITY_DIMS
self._volatility = VOLATILITY_DIMS
self._flow = FLOW_DIMS
self._structure = STRUCTURE_DIMS
self._generated: Set[str] = set()
def generate_regimes(
self,
max_regimes: int = 200,
seed: int = 42,
) -> List[ExpandedRegime]:
"""
Generate distinct, non-overlapping regimes from dimension combinations.
Uses stratified sampling to ensure diversity.
"""
import random
rng = random.Random(seed)
regimes: List[ExpandedRegime] = []
# Generate all combinations (stratified)
combos = list(itertools.product(
self._liquidity, self._volatility, self._flow, self._structure,
))
# Shuffle and take max_regimes
rng.shuffle(combos)
combos = combos[:max_regimes]
for i, (liq, vol, flow, struct) in enumerate(combos):
# Calculate bid/ask from dimensions
spread = vol.spread_bps
mid = 50000.0
bid = mid - spread / 2
ask = mid + spread / 2
regime = ExpandedRegime(
regime_id=f"exp_{i:03d}",
label=f"{liq.label}_{vol.label}_{flow.label}_{struct.label}",
liquidity=liq,
volatility=vol,
flow=flow,
structure=struct,
bid=bid,
ask=ask,
)
regimes.append(regime)
self._generated.add(regime.regime_id)
return regimes
def regime_to_scenario(
self,
regime: ExpandedRegime,
symbol: str = "BTCUSDT",
steps: int = 20,
seed: int = 42,
) -> "Scenario":
"""Convert an ExpandedRegime to a Scenario for evaluation."""
from malkhut.training.cma_trainer import Scenario
venue = VenueRules(
exchange="bingx", symbol=symbol, tick_size=0.1, lot_size=0.001,
min_qty=0.001, min_notional=5.0, maker_fee_bps=-0.2, taker_fee_bps=0.5,
post_only_supported=True, reduce_only_supported=True,
max_orders_per_second=100, max_cancels_per_minute=120,
)
book = OrderBookState(
ts_ns=1_000_000_000, symbol=symbol,
bids=(PriceLevel(regime.bid, regime.liquidity.bid_qty),),
asks=(PriceLevel(regime.ask, regime.liquidity.ask_qty),),
)
account = AccountState(
ts_ns=1_000_000_000, equity=10000.0, wallet_balance=10000.0,
available_balance=10000.0, margin_used=0.0, total_notional=0.0,
)
state = MarketWorldState(
ts_ns=1_000_000_000, mode=Mode.ENDOGENOUS_AGENT_SIM,
venue=venue, book=book, account=account,
)
return Scenario(
scenario_id=regime.regime_id,
symbol=symbol,
initial_state=state,
counterparties=regime.structure.counterparties,
max_steps=steps,
tags=(regime.label,),
)
@property
def generated_count(self) -> int:
return len(self._generated)