Files
sentiment-engine/MALKHUT/malkhut/training/asset_behavior.py
Codex 981b469d51 malkhut: asset classification, behavior DSL, auto-compiler, behavior-driven scenarios
Cambrian Explosion Phase 0 complete:
- Multi-label invariant asset taxonomy (13 assets, 10 sectors, 6 roles)
- Asset Behavior DSL: 10 orthogonal dimensions per asset, 3 composable
  templates, research-validated from live Binance/BingX API data
- Asset Compiler: auto-fetch from Binance public API, compute profiles,
  rate-limited (1 req/s), cached, 28 known classifications
- ScenarioFactory: all 30 scenario types use behavior-driven prices
  (BTC=$64K, ETH=$1.8K, SOL=$80, DOGE=$0.07) instead of hardcoded
  BTC prices. Auto-compiles unknown assets on demand.
- Label query interfaces: build_suite_for_sector/role/template/vol/labels
- 190 exhaustive tests for asset classification (up from 66)
- 1140 tests all green, CWM throughput 189K calls/s (121% of baseline)
- Comprehensive README: 1043 lines with full documentation

Research sources: Binance live REST API, BingX open API, CoinGlass,
academic literature (Bouchaud, Cont/Stoikov, Cartea/Jaimungal)
2026-07-11 06:37:17 +02:00

399 lines
14 KiB
Python

"""
Asset Behavior DSL — Pure Python composable behavior definitions.
Decomposes asset behavior into orthogonal dimensions, each with
empirically-validated parameters from live Binance/BingX API + academic
literature (Bouchaud, Cont/Stoikov, Cartea/Jaimungal).
13 pre-defined assets, 3 composable templates, 10 orthogonal dimensions.
Auto-compilable via asset_compiler.py for any Binance-listed symbol.
Usage:
from malkhut.training.asset_behavior import get_behavior, ASSET_BEHAVIORS
btc = get_behavior("BTCUSDT")
print(btc.depth.amplitude_usd) # $750,000
print(btc.expected_slippage_bps(100_000)) # ~2 bps
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, List, Optional
from malkhut.training.asset_classification import Sector, TokenRole
@dataclass(frozen=True, slots=True)
class DepthProfile:
amplitude_usd: float
alpha: float
fragility_factor: float
depth_at_10bps_usd: float
depth_at_100bps_usd: float
@dataclass(frozen=True, slots=True)
class SpreadProfile:
normal_bps: float
stress_multiplier: float
@dataclass(frozen=True, slots=True)
class FlowProfile:
orders_per_sec_normal: float
orders_per_sec_stress: float
cancel_fill_ratio: float
median_order_usd: float
p99_order_usd: float
avg_trade_usd: float
@dataclass(frozen=True, slots=True)
class VolatilityProfile:
annualized_normal: float
annualized_crisis: float
garch_alpha: float
garch_beta: float
half_life_hours: float
@dataclass(frozen=True, slots=True)
class IntradayProfile:
peak_hour_utc: int
trough_hour_utc: int
ratio: float
@dataclass(frozen=True, slots=True)
class WeekendProfile:
vol_mult: float
volume_mult: float
spread_mult: float
@dataclass(frozen=True, slots=True)
class CorrelationProfile:
eth_beta: float
btc_corr_normal: float
btc_corr_crash: float
@dataclass(frozen=True, slots=True)
class MarketMakerProfile:
max_inventory_usd: float
skew_tolerance_bps: float
pull_speed_ms: float
margin_bps: float
@dataclass(frozen=True, slots=True)
class LiquidationProfile:
oi_mcap_ratio: float
trigger_pct: float
speed: str
recovery: str
@dataclass(frozen=True, slots=True)
class FundingProfile:
mean_bps_8h: float
std_bps_8h: float
positive_pct: float
basis_typical_bps: float
@dataclass(frozen=True, slots=True)
class RetailProfile:
ratio: float
inst_gap: float
@dataclass(frozen=True, slots=True)
class BingxProfile:
spread_mult: float
depth_ratio: float
latency_ms: float
taker_fee_bps: float
maker_fee_bps: float
funding_lag_hours: float
@dataclass(frozen=True, slots=True)
class BehaviorTemplate:
name: str
depth: DepthProfile
spread: SpreadProfile
flow: FlowProfile
vol: VolatilityProfile
intraday: IntradayProfile
weekend: WeekendProfile
correlation: CorrelationProfile
market_maker: MarketMakerProfile
liquidation: LiquidationProfile
funding: FundingProfile
retail: RetailProfile
bingx: BingxProfile
@dataclass(frozen=True, slots=True)
class AssetBehavior:
symbol: str
depth: DepthProfile
spread: SpreadProfile
flow: FlowProfile
vol: VolatilityProfile
intraday: IntradayProfile
weekend: WeekendProfile
correlation: CorrelationProfile
market_maker: MarketMakerProfile
liquidation: LiquidationProfile
funding: FundingProfile
retail: RetailProfile
bingx: BingxProfile
template_name: str = ""
reference_price: float = 0.0
@classmethod
def from_template(cls, symbol: str, template: BehaviorTemplate,
overrides: Optional[Dict[str, object]] = None,
reference_price: float = 0.0) -> AssetBehavior:
params = {
"depth": template.depth, "spread": template.spread,
"flow": template.flow, "vol": template.vol,
"intraday": template.intraday, "weekend": template.weekend,
"correlation": template.correlation, "market_maker": template.market_maker,
"liquidation": template.liquidation, "funding": template.funding,
"retail": template.retail, "bingx": template.bingx,
}
if overrides:
for key, value in overrides.items():
if key in params:
if isinstance(value, dict):
base = params[key]
params[key] = type(base)(**{**base.__dict__, **value})
else:
params[key] = value
return cls(symbol=symbol, template_name=template.name,
reference_price=reference_price, **params)
def depth_at_bps(self, bps: float) -> float:
"""D(d) = amplitude * d^(1-alpha)"""
return self.depth.amplitude_usd * (bps ** (1.0 - self.depth.alpha))
def expected_slippage_bps(self, order_usd: float) -> float:
"""Estimate slippage for a market order of given notional."""
d = 1.0
cumulative = 0.0
while cumulative < order_usd and d < 1000:
cumulative += self.depth.amplitude_usd * (d ** (-self.depth.alpha))
d += 1.0
return d if cumulative >= order_usd else 1000.0
TEMPLATES: Dict[str, BehaviorTemplate] = {}
def _t(name: str, **kwargs) -> BehaviorTemplate:
t = BehaviorTemplate(name=name, **kwargs)
TEMPLATES[name] = t
return t
INSTITUTIONAL_BLUE_CHIP = _t("institutional_blue_chip",
depth=DepthProfile(1_500_000, 0.70, 0.10, 7_000_000, 20_000_000),
spread=SpreadProfile(0.01, 50.0),
flow=FlowProfile(300, 5000, 20.0, 643, 200_000, 5_000),
vol=VolatilityProfile(35.0, 100.0, 0.10, 0.88, 48),
intraday=IntradayProfile(15, 19, 7.4),
weekend=WeekendProfile(0.65, 0.52, 1.20),
correlation=CorrelationProfile(1.00, 1.00, 1.00),
market_maker=MarketMakerProfile(10_000_000, 15, 3.0, 0.5),
liquidation=LiquidationProfile(0.005, 6.5, "slow", "fast"),
funding=FundingProfile(0.59, 0.22, 100.0, 4.0),
retail=RetailProfile(0.35, 0.04),
bingx=BingxProfile(12.6, 0.048, 100, 5.0, 2.0, 4))
MID_CAP_L1 = _t("mid_cap_l1",
depth=DepthProfile(200_000, 0.85, 0.15, 800_000, 3_000_000),
spread=SpreadProfile(1.5, 10.0),
flow=FlowProfile(100, 1500, 10.0, 800, 150_000, 2_000),
vol=VolatilityProfile(75.0, 150.0, 0.12, 0.85, 36),
intraday=IntradayProfile(15, 11, 5.0),
weekend=WeekendProfile(0.70, 0.55, 1.25),
correlation=CorrelationProfile(0.85, 0.60, 0.90),
market_maker=MarketMakerProfile(2_000_000, 25, 15.0, 1.0),
liquidation=LiquidationProfile(0.015, 5.0, "medium", "medium"),
funding=FundingProfile(0.10, 0.30, 60.0, 3.0),
retail=RetailProfile(0.65, 0.50),
bingx=BingxProfile(3.0, 0.30, 100, 5.0, 2.0, 4))
RETAIL_MEME = _t("retail_meme",
depth=DepthProfile(25_000, 1.00, 0.30, 100_000, 2_500_000),
spread=SpreadProfile(1.35, 15.0),
flow=FlowProfile(80, 800, 8.0, 96, 52_000, 200),
vol=VolatilityProfile(78.0, 200.0, 0.15, 0.82, 24),
intraday=IntradayProfile(15, 10, 4.5),
weekend=WeekendProfile(0.70, 0.50, 1.30),
correlation=CorrelationProfile(0.83, 0.45, 0.80),
market_maker=MarketMakerProfile(500_000, 40, 25.0, 2.0),
liquidation=LiquidationProfile(0.014, 4.0, "fast", "slow"),
funding=FundingProfile(0.39, 0.34, 80.0, 2.0),
retail=RetailProfile(0.80, 0.31),
bingx=BingxProfile(7.0, 1.27, 100, 5.0, 2.0, 4))
ASSET_BEHAVIORS: Dict[str, AssetBehavior] = {}
def _b(symbol: str, template_name: str, reference_price: float = 0.0,
**overrides) -> AssetBehavior:
tmpl = TEMPLATES[template_name]
b = AssetBehavior.from_template(symbol, tmpl, overrides or None, reference_price=reference_price)
ASSET_BEHAVIORS[symbol] = b
return b
BTC = _b("BTCUSDT", "institutional_blue_chip", reference_price=64000.0,
depth=DepthProfile(750_000, 0.70, 0.10, 5_983_000, 20_000_000),
vol=VolatilityProfile(35.0, 100.0, 0.10, 0.88, 48),
bingx=BingxProfile(12.6, 0.048, 100, 5.0, 2.0, 4))
ETH = _b("ETHUSDT", "institutional_blue_chip", reference_price=1800.0,
depth=DepthProfile(600_000, 0.75, 0.12, 2_095_000, 7_200_000),
vol=VolatilityProfile(66.5, 130.0, 0.12, 0.86, 40),
correlation=CorrelationProfile(1.00, 0.90, 0.97),
liquidation=LiquidationProfile(0.019, 4.0, "medium", "medium"),
bingx=BingxProfile(9.0, 9.06, 100, 5.0, 2.0, 4))
SOL = _b("SOLUSDT", "mid_cap_l1", reference_price=80.0,
depth=DepthProfile(400_000, 0.85, 0.18, 954_000, 4_000_000),
spread=SpreadProfile(1.26, 8.0),
vol=VolatilityProfile(71.9, 150.0, 0.13, 0.84, 32),
correlation=CorrelationProfile(0.88, 0.65, 0.92),
retail=RetailProfile(0.72, 0.75),
bingx=BingxProfile(1.7, 0.33, 80, 5.0, 2.0, 2))
DOGE = _b("DOGEUSDT", "retail_meme", reference_price=0.07,
depth=DepthProfile(22_000, 1.00, 0.30, 432_000, 2_772_000),
vol=VolatilityProfile(77.9, 200.0, 0.15, 0.82, 24),
correlation=CorrelationProfile(0.83, 0.45, 0.80),
bingx=BingxProfile(7.0, 1.27, 100, 5.0, 2.0, 4))
ADA = _b("ADAUSDT", "mid_cap_l1", reference_price=0.17,
depth=DepthProfile(60_000, 0.90, 0.20, 110_000, 1_500_000),
spread=SpreadProfile(5.95, 12.0),
vol=VolatilityProfile(78.7, 160.0, 0.14, 0.83, 30),
bingx=BingxProfile(8.0, 0.20, 120, 5.0, 2.0, 4))
AVAX = _b("AVAXUSDT", "mid_cap_l1", reference_price=7.0,
depth=DepthProfile(55_000, 0.88, 0.18, 107_000, 1_200_000),
spread=SpreadProfile(1.48, 10.0),
vol=VolatilityProfile(80.1, 160.0, 0.13, 0.84, 32),
correlation=CorrelationProfile(0.79, 0.55, 0.88),
bingx=BingxProfile(4.0, 0.25, 110, 5.0, 2.0, 4))
UNI = _b("UNIUSDT", "mid_cap_l1", reference_price=3.6,
depth=DepthProfile(30_000, 0.95, 0.25, 53_000, 800_000),
spread=SpreadProfile(2.76, 12.0),
vol=VolatilityProfile(95.3, 220.0, 0.16, 0.81, 22),
correlation=CorrelationProfile(0.70, 0.50, 0.85),
market_maker=MarketMakerProfile(300_000, 50, 30.0, 2.5),
retail=RetailProfile(0.60, 0.25),
bingx=BingxProfile(10.0, 0.15, 150, 5.0, 2.0, 6))
LINK = _b("LINKUSDT", "mid_cap_l1", reference_price=8.0,
depth=DepthProfile(80_000, 0.87, 0.17, 129_000, 1_800_000),
spread=SpreadProfile(1.25, 8.0),
vol=VolatilityProfile(77.3, 155.0, 0.12, 0.85, 34),
correlation=CorrelationProfile(0.88, 0.62, 0.91),
bingx=BingxProfile(3.5, 0.28, 100, 5.0, 2.0, 4))
BNB = _b("BNBUSDT", "institutional_blue_chip", reference_price=575.0,
depth=DepthProfile(500_000, 0.78, 0.12, 2_000_000, 8_000_000),
spread=SpreadProfile(0.50, 15.0),
vol=VolatilityProfile(50.0, 120.0, 0.11, 0.87, 42),
funding=FundingProfile(0.40, 0.25, 90.0, 3.0),
retail=RetailProfile(0.50, 0.20),
bingx=BingxProfile(5.0, 0.50, 100, 5.0, 2.0, 4))
MATIC = _b("MATICUSDT", "mid_cap_l1", reference_price=0.5,
depth=DepthProfile(35_000, 0.92, 0.22, 55_000, 900_000),
spread=SpreadProfile(1.50, 10.0),
vol=VolatilityProfile(82.0, 170.0, 0.14, 0.83, 28),
correlation=CorrelationProfile(0.82, 0.58, 0.89),
bingx=BingxProfile(5.0, 0.30, 110, 5.0, 2.0, 4))
AAVE = _b("AAVEUSDT", "mid_cap_l1", reference_price=100.0,
depth=DepthProfile(20_000, 0.95, 0.25, 35_000, 600_000),
spread=SpreadProfile(2.50, 12.0),
vol=VolatilityProfile(85.0, 200.0, 0.15, 0.82, 26),
correlation=CorrelationProfile(0.75, 0.52, 0.86),
market_maker=MarketMakerProfile(200_000, 45, 28.0, 2.0),
retail=RetailProfile(0.55, 0.20),
bingx=BingxProfile(10.0, 0.12, 140, 5.0, 2.0, 6))
DOT = _b("DOTUSDT", "mid_cap_l1", reference_price=6.0,
depth=DepthProfile(70_000, 0.88, 0.18, 120_000, 1_500_000),
spread=SpreadProfile(1.00, 8.0),
vol=VolatilityProfile(72.0, 145.0, 0.12, 0.85, 34),
bingx=BingxProfile(4.0, 0.30, 110, 5.0, 2.0, 4))
ATOM = _b("ATOMUSDT", "mid_cap_l1", reference_price=8.0,
depth=DepthProfile(25_000, 0.92, 0.22, 45_000, 700_000),
spread=SpreadProfile(2.00, 10.0),
vol=VolatilityProfile(76.0, 160.0, 0.13, 0.84, 30),
correlation=CorrelationProfile(0.78, 0.48, 0.87),
bingx=BingxProfile(6.0, 0.20, 130, 5.0, 2.0, 5))
# ==============================================================================
# Query functions
# ==============================================================================
def get_behavior(symbol: str) -> Optional[AssetBehavior]:
return ASSET_BEHAVIORS.get(symbol)
def list_behavior_symbols() -> List[str]:
return list(ASSET_BEHAVIORS.keys())
def get_behaviors_by_sector(sector: Sector) -> List[AssetBehavior]:
from malkhut.training.asset_classification import get_assets_by_sector
return [ASSET_BEHAVIORS[p.symbol] for p in get_assets_by_sector(sector)
if p.symbol in ASSET_BEHAVIORS]
def get_behaviors_by_role(role: TokenRole) -> List[AssetBehavior]:
from malkhut.training.asset_classification import get_assets_by_token_role
return [ASSET_BEHAVIORS[p.symbol] for p in get_assets_by_token_role(role)
if p.symbol in ASSET_BEHAVIORS]
def get_behaviors_by_template(template_name: str) -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values() if b.template_name == template_name]
def get_behaviors_by_volatility_band(min_ann: float = 0.0, max_ann: float = 500.0) -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values()
if min_ann <= b.vol.annualized_normal <= max_ann]
def get_fast_cascade_assets() -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values() if b.liquidation.speed == "fast"]
def get_institutional_assets() -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values() if b.retail.ratio < 0.5]
def get_retail_dominated_assets() -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values() if b.retail.ratio >= 0.6]
def get_high_vol_assets() -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values() if b.vol.annualized_normal >= 75.0]
def get_thin_book_assets() -> List[AssetBehavior]:
return [b for b in ASSET_BEHAVIORS.values() if b.depth.amplitude_usd < 100_000]