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)
This commit is contained in:
Codex
2026-07-11 06:37:17 +02:00
parent 3d313290b3
commit 981b469d51
6 changed files with 4823 additions and 0 deletions

View File

@@ -0,0 +1,530 @@
"""
Asset Classification — INVARIANT characteristics only.
Split into two axes:
FUNDAMENTAL — intrinsic to the token's design (never changes):
Sector*, TokenRole*, SupplyModel, Consensus, SmartContractCapability
(* multi-label: an asset CAN belong to multiple sectors/roles)
TECHNICAL — invariant market-structure properties (set at listing, rarely change):
MarketCapTier, TypicalSpread/Depth, TickSize/LotSize, FeeStructure,
DerivativeAccess, PriceDecimals, TypicalVolume
Overlap handling (industry standard, per CoinGecko/CMC/Messari):
- Sector & TokenRole: frozenset (multi-label). An asset can be LAYER1 + DEFI,
or GAS + GOVERNANCE. The FIRST element is "primary" for single-label compat.
- Consensus, SupplyModel, MarketCapTier, SmartContractCapability: single enum.
One chain = one consensus. One supply mechanism. Inherently singular.
Sources: CoinGecko/CMC sector taxonomy (multi-tag per coin), Messari framework,
BIS/IMF digital asset classification, MiCA regulatory taxonomy,
academic factor models (Cong & He 2019, Harvey/Ramachandran/Santoro 2021),
exchange listing standards (Binance, BingX).
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Sequence
# ==============================================================================
# FUNDAMENTAL dimensions — intrinsic to the token, never change
# ==============================================================================
class Sector(str, Enum):
"""Primary use-case / industry vertical. CoinGecko + Messari + CMC consensus.
Multi-label: an asset CAN appear in multiple sectors (e.g. BNB = EXCHANGE + LAYER1)."""
CURRENCY = "currency" # BTC: pure peer-to-peer money
LAYER1 = "layer1" # ETH, SOL, ADA, AVAX, DOT, ATOM: smart-contract platform
LAYER2 = "layer2" # MATIC, ARB, OP: scaling solutions
DEFI = "defi" # UNI, AAVE, MKR: decentralized finance protocols
ORACLE = "oracle" # LINK: external data feeds
EXCHANGE = "exchange" # BNB, OKB: CEX utility tokens
MEME = "meme" # DOGE, SHIB: community/speculation driven
PRIVACY = "privacy" # XMR, ZEC: privacy-preserving chains
STORAGE = "storage" # FIL, AR: decentralized storage
GAMING_NFT = "gaming_nft" # AXS, SAND, IMX: gaming/metaverse
class TokenRole(str, Enum):
"""Functional role of the token within its ecosystem. Determines demand elasticity.
Multi-label: an asset CAN serve multiple roles (e.g. ETH = GAS + STORE_OF_VALUE + GOVERNANCE)."""
GAS = "gas" # Must hold to pay tx fees (ETH, SOL, ADA, AVAX, DOT, ATOM, BNB)
STORE_OF_VALUE = "store_of_value" # Digital gold narrative (BTC, ETH)
GOVERNANCE = "governance" # Voting rights (UNI, AAVE, MKR, DOT)
UTILITY = "utility" # Pays for a service (LINK data, FIL storage)
MEME = "meme" # No utility, pure speculation (DOGE)
EXCHANGE_FEE = "exchange_fee" # Fee discount / burn (BNB)
class SupplyModel(str, Enum):
"""How new tokens enter circulation. Determines long-term supply pressure.
Single-label: one asset = one supply mechanism."""
FIXED_CAP = "fixed_cap" # Hard cap: BTC (21M), AAVE (16M)
DISINFLATIONARY = "disinflationary" # Issuance decreases over time (ETH post-merge)
INFLATIONARY = "inflationary" # Ongoing issuance (SOL ~5%, ADA ~3%, AVAX ~4%)
BURN_MECHANISM = "burn_mechanism" # Buyback-and-burn (BNB quarterly burns)
class ConsensusFamily(str, Enum):
"""Consensus mechanism. Determines miner/validator selling behaviour.
Single-label: one chain = one consensus."""
POW = "pow" # Must sell to cover electricity → constant sell pressure
POS = "pos" # Can hold, staking yield → lower forced selling
DPOS = "dpos" # Delegated PoS (EOS, TRX, BNB Chain) → concentrated validator set
class SmartContractCapability(str, Enum):
"""Ability to run arbitrary smart contracts. Determines DeFi composability.
Single-label: one chain = one capability level."""
FULL = "full" # EVM or equivalent (ETH, SOL, ADA, AVAX, DOT, ATOM, UNI, AAVE)
PARTIAL = "partial" # Limited scripting (BNB Chain — EVM-compatible but different governance)
NONE = "none" # No smart contracts (BTC, DOGE)
# ==============================================================================
# TECHNICAL dimensions — invariant market-structure properties
# ==============================================================================
class MarketCapTier(str, Enum):
"""Absolute market-cap band. Changes slowly → semi-invariant. Determines price-impact per dollar."""
MEGA = "mega" # >$500B (BTC)
LARGE = "large" # $50-500B (ETH, BNB)
MID = "mid" # $5-50B (SOL, ADA, AVAX, DOT, ATOM)
SMALL = "small" # $500M-5B (UNI, AAVE, LINK, MATIC)
MICRO = "micro" # <$500M
class DerivativeAccess(str, Enum):
"""Derivatives availability. Affects shorting, funding dynamics, price discovery.
Single-label: one asset = one access level on a given exchange."""
PERPS_AND_OPTIONS = "perps_and_options" # BTC, ETH
PERPS_ONLY = "perps_only" # Most mid/small caps
NONE = "none" # Cannot be shorted on CEX
class VolatilityProfile(str, Enum):
"""Long-run average volatility band. Semi-invariant statistical fingerprint."""
LOW = "low" # <30% annualized (BTC, stablecoins)
MEDIUM = "medium" # 30-80% (ETH, ADA)
HIGH = "high" # 80-150% (SOL, DOGE, AVAX)
EXTREME = "extreme" # >150% (micro-caps, new listings)
class LiquidityProfile(str, Enum):
"""Long-run average liquidity tier. Semi-invariant order-book fingerprint."""
DEEP = "deep" # >$100M daily (BTC, ETH)
NORMAL = "normal" # $10-100M (SOL, BNB, DOGE)
THIN = "thin" # $1-10M (UNI, AAVE, MATIC)
ILLIQUID = "illiquid" # <$1M
# ==============================================================================
# AssetProfile — frozen, all-invariant, multi-label where appropriate
# ==============================================================================
@dataclass(frozen=True, slots=True)
class AssetProfile:
"""Immutable asset classification. Sector & TokenRole are frozensets (multi-label).
First element = primary label for single-label backward compatibility."""
# --- Identity ---
symbol: str
# --- Fundamental (intrinsic, never changes) ---
sectors: tuple[Sector, ...] # multi-label: BNB = (EXCHANGE, LAYER1). First = primary.
token_roles: tuple[TokenRole, ...] # multi-label: ETH = (GAS, STORE_OF_VALUE, GOVERNANCE). First = primary.
supply_model: SupplyModel # single-label
consensus: ConsensusFamily # single-label
smart_contracts: SmartContractCapability # single-label
# --- Technical (invariant market-structure properties) ---
market_cap_tier: MarketCapTier
volatility_profile: VolatilityProfile
liquidity_profile: LiquidityProfile
derivative_access: DerivativeAccess
# --- Execution parameters (exchange-set, invariant) ---
tick_size: float
lot_size: float
price_decimals: int
maker_fee_bps: float
taker_fee_bps: float
# --- Order-book fingerprint (long-run averages, semi-invariant) ---
typical_spread_bps: float
typical_depth_usd: float
typical_daily_volume_usd: float
# --- Structural flags (invariant) ---
has_funding: bool = False
has_options: bool = False
@property
def sector(self) -> Sector:
"""Primary sector (first in tuple). For single-label consumers."""
return self.sectors[0]
@property
def token_role(self) -> TokenRole:
"""Primary role (first in tuple). For single-label consumers."""
return self.token_roles[0]
@property
def is_gas_token(self) -> bool:
return TokenRole.GAS in self.token_roles
@property
def is_pure_currency(self) -> bool:
return Sector.CURRENCY in self.sectors
@property
def can_be_shorted(self) -> bool:
return self.derivative_access != DerivativeAccess.NONE
@property
def supply_pressure(self) -> str:
"""Predictive label: 'forced' for PoW miners who must sell,
'optional' for PoS validators who can hold."""
if self.consensus == ConsensusFamily.POW:
return "forced"
return "optional"
@property
def demand_elasticity(self) -> str:
"""Predictive label: 'inelastic' for gas/store-of-value (must hold),
'elastic' for governance/meme/exchange (can choose not to buy)."""
if any(r in self.token_roles for r in (TokenRole.GAS, TokenRole.STORE_OF_VALUE)):
return "inelastic"
return "elastic"
# ==============================================================================
# Helper to build multi-label profiles concisely
# ==============================================================================
def _profile(
symbol: str,
sectors: Sequence[Sector],
token_roles: Sequence[TokenRole],
supply_model: SupplyModel,
consensus: ConsensusFamily,
smart_contracts: SmartContractCapability,
market_cap_tier: MarketCapTier,
volatility_profile: VolatilityProfile,
liquidity_profile: LiquidityProfile,
derivative_access: DerivativeAccess,
tick_size: float,
lot_size: float,
price_decimals: int,
maker_fee_bps: float,
taker_fee_bps: float,
typical_spread_bps: float,
typical_depth_usd: float,
typical_daily_volume_usd: float,
has_funding: bool = False,
has_options: bool = False,
) -> AssetProfile:
return AssetProfile(
symbol=symbol,
sectors=tuple(sectors),
token_roles=tuple(token_roles),
supply_model=supply_model,
consensus=consensus,
smart_contracts=smart_contracts,
market_cap_tier=market_cap_tier,
volatility_profile=volatility_profile,
liquidity_profile=liquidity_profile,
derivative_access=derivative_access,
tick_size=tick_size, lot_size=lot_size, price_decimals=price_decimals,
maker_fee_bps=maker_fee_bps, taker_fee_bps=taker_fee_bps,
typical_spread_bps=typical_spread_bps,
typical_depth_usd=typical_depth_usd,
typical_daily_volume_usd=typical_daily_volume_usd,
has_funding=has_funding, has_options=has_options,
)
# ==============================================================================
# Profiles — 13 major crypto assets with BingX perps
# ==============================================================================
ASSET_PROFILES: Dict[str, AssetProfile] = {
"BTCUSDT": _profile(
symbol="BTCUSDT",
sectors=[Sector.CURRENCY],
token_roles=[TokenRole.STORE_OF_VALUE],
supply_model=SupplyModel.FIXED_CAP, consensus=ConsensusFamily.POW,
smart_contracts=SmartContractCapability.NONE,
market_cap_tier=MarketCapTier.MEGA,
volatility_profile=VolatilityProfile.LOW,
liquidity_profile=LiquidityProfile.DEEP,
derivative_access=DerivativeAccess.PERPS_AND_OPTIONS,
tick_size=0.1, lot_size=0.001, price_decimals=1,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=0.3, typical_depth_usd=5_000_000,
typical_daily_volume_usd=30_000_000_000,
has_funding=True, has_options=True,
),
"ETHUSDT": _profile(
symbol="ETHUSDT",
sectors=[Sector.LAYER1, Sector.DEFI],
token_roles=[TokenRole.GAS, TokenRole.STORE_OF_VALUE, TokenRole.GOVERNANCE],
supply_model=SupplyModel.DISINFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.LARGE,
volatility_profile=VolatilityProfile.MEDIUM,
liquidity_profile=LiquidityProfile.DEEP,
derivative_access=DerivativeAccess.PERPS_AND_OPTIONS,
tick_size=0.01, lot_size=0.001, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=0.5, typical_depth_usd=3_000_000,
typical_daily_volume_usd=15_000_000_000,
has_funding=True, has_options=True,
),
"SOLUSDT": _profile(
symbol="SOLUSDT",
sectors=[Sector.LAYER1],
token_roles=[TokenRole.GAS],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.MID,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.NORMAL,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.01, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=1.0, typical_depth_usd=1_000_000,
typical_daily_volume_usd=3_000_000_000,
has_funding=True,
),
"DOGEUSDT": _profile(
symbol="DOGEUSDT",
sectors=[Sector.MEME, Sector.CURRENCY],
token_roles=[TokenRole.MEME, TokenRole.GAS],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POW,
smart_contracts=SmartContractCapability.NONE,
market_cap_tier=MarketCapTier.MID,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.NORMAL,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.00001, lot_size=1.0, price_decimals=5,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=2.0, typical_depth_usd=500_000,
typical_daily_volume_usd=1_000_000_000,
has_funding=True,
),
"ADAUSDT": _profile(
symbol="ADAUSDT",
sectors=[Sector.LAYER1],
token_roles=[TokenRole.GAS],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.DPOS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.MID,
volatility_profile=VolatilityProfile.MEDIUM,
liquidity_profile=LiquidityProfile.NORMAL,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.0001, lot_size=1.0, price_decimals=4,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=1.0, typical_depth_usd=800_000,
typical_daily_volume_usd=500_000_000,
has_funding=True,
),
"AVAXUSDT": _profile(
symbol="AVAXUSDT",
sectors=[Sector.LAYER1, Sector.DEFI],
token_roles=[TokenRole.GAS],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.MID,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.NORMAL,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.01, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=1.2, typical_depth_usd=600_000,
typical_daily_volume_usd=400_000_000,
has_funding=True,
),
"UNIUSDT": _profile(
symbol="UNIUSDT",
sectors=[Sector.DEFI],
token_roles=[TokenRole.GOVERNANCE, TokenRole.UTILITY],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.SMALL,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.THIN,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.01, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=2.0, typical_depth_usd=300_000,
typical_daily_volume_usd=200_000_000,
has_funding=True,
),
"LINKUSDT": _profile(
symbol="LINKUSDT",
sectors=[Sector.ORACLE],
token_roles=[TokenRole.UTILITY],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.SMALL,
volatility_profile=VolatilityProfile.MEDIUM,
liquidity_profile=LiquidityProfile.THIN,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.01, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=1.0, typical_depth_usd=700_000,
typical_daily_volume_usd=400_000_000,
has_funding=True,
),
"BNBUSDT": _profile(
symbol="BNBUSDT",
sectors=[Sector.EXCHANGE, Sector.LAYER1],
token_roles=[TokenRole.EXCHANGE_FEE, TokenRole.GAS],
supply_model=SupplyModel.BURN_MECHANISM, consensus=ConsensusFamily.DPOS,
smart_contracts=SmartContractCapability.PARTIAL,
market_cap_tier=MarketCapTier.LARGE,
volatility_profile=VolatilityProfile.MEDIUM,
liquidity_profile=LiquidityProfile.DEEP,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.001, price_decimals=2,
maker_fee_bps=-0.1, taker_fee_bps=0.4,
typical_spread_bps=0.5, typical_depth_usd=2_000_000,
typical_daily_volume_usd=2_000_000_000,
has_funding=True,
),
"MATICUSDT": _profile(
symbol="MATICUSDT",
sectors=[Sector.LAYER2],
token_roles=[TokenRole.GAS],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.SMALL,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.THIN,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.0001, lot_size=1.0, price_decimals=4,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=1.5, typical_depth_usd=500_000,
typical_daily_volume_usd=300_000_000,
has_funding=True,
),
"AAVEUSDT": _profile(
symbol="AAVEUSDT",
sectors=[Sector.DEFI],
token_roles=[TokenRole.GOVERNANCE],
supply_model=SupplyModel.FIXED_CAP, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.SMALL,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.THIN,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.01, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=2.5, typical_depth_usd=200_000,
typical_daily_volume_usd=150_000_000,
has_funding=True,
),
"DOTUSDT": _profile(
symbol="DOTUSDT",
sectors=[Sector.LAYER1],
token_roles=[TokenRole.GAS, TokenRole.GOVERNANCE],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.DPOS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.MID,
volatility_profile=VolatilityProfile.MEDIUM,
liquidity_profile=LiquidityProfile.NORMAL,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.001, lot_size=0.1, price_decimals=3,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=1.0, typical_depth_usd=600_000,
typical_daily_volume_usd=300_000_000,
has_funding=True,
),
"ATOMUSDT": _profile(
symbol="ATOMUSDT",
sectors=[Sector.LAYER1],
token_roles=[TokenRole.GAS],
supply_model=SupplyModel.INFLATIONARY, consensus=ConsensusFamily.POS,
smart_contracts=SmartContractCapability.FULL,
market_cap_tier=MarketCapTier.MID,
volatility_profile=VolatilityProfile.HIGH,
liquidity_profile=LiquidityProfile.THIN,
derivative_access=DerivativeAccess.PERPS_ONLY,
tick_size=0.01, lot_size=0.01, price_decimals=2,
maker_fee_bps=-0.2, taker_fee_bps=0.5,
typical_spread_bps=2.0, typical_depth_usd=250_000,
typical_daily_volume_usd=100_000_000,
has_funding=True,
),
}
# ==============================================================================
# Query functions — filter by any dimension (multi-label aware)
# ==============================================================================
def get_asset_profile(symbol: str) -> Optional[AssetProfile]:
return ASSET_PROFILES.get(symbol)
def list_assets() -> List[str]:
return list(ASSET_PROFILES.keys())
def get_assets_by_sector(sector: Sector) -> List[AssetProfile]:
"""Match assets where the queried sector is ANY of their sectors."""
return [p for p in ASSET_PROFILES.values() if sector in p.sectors]
def get_assets_by_token_role(role: TokenRole) -> List[AssetProfile]:
"""Match assets where the queried role is ANY of their roles."""
return [p for p in ASSET_PROFILES.values() if role in p.token_roles]
def get_assets_by_supply(model: SupplyModel) -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.supply_model == model]
def get_assets_by_consensus(family: ConsensusFamily) -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.consensus == family]
def get_assets_by_market_cap(tier: MarketCapTier) -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.market_cap_tier == tier]
def get_assets_by_volatility(vol: VolatilityProfile) -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.volatility_profile == vol]
def get_assets_by_liquidity(liq: LiquidityProfile) -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.liquidity_profile == liq]
def get_assets_by_derivatives(access: DerivativeAccess) -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.derivative_access == access]
def get_gas_tokens() -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if TokenRole.GAS in p.token_roles]
def get_pov_assets() -> List[AssetProfile]:
"""Assets with forced sell pressure (PoW miners)."""
return [p for p in ASSET_PROFILES.values() if p.supply_pressure == "forced"]
def get_shortable_assets() -> List[AssetProfile]:
return [p for p in ASSET_PROFILES.values() if p.can_be_shorted]
def get_multi_sector_assets() -> List[AssetProfile]:
"""Assets belonging to more than one sector."""
return [p for p in ASSET_PROFILES.values() if len(p.sectors) > 1]
def get_multi_role_assets() -> List[AssetProfile]:
"""Assets serving more than one token role."""
return [p for p in ASSET_PROFILES.values() if len(p.token_roles) > 1]