ExchangeProfile: standardized exchange metadata (fees, latency, capabilities). 3 pre-defined exchanges: Binance, BingX, Bybit. AssetProfile.exchanges: tuple[str] — which venues trade each asset. New query functions: get_assets_on_exchange, get_common_assets, get_exchange_for_asset, get_exchange, list_exchanges. _DATA_STORAGE_SCHEMA_FORMATS.md: comprehensive reference for agent consumption — data model, storage format, query interfaces, data flow diagram, enum reference, import patterns for BLUE/VIOLET/UV integration. README updated: exchange registry section, package structure, subsystems table.
617 lines
25 KiB
Python
617 lines
25 KiB
Python
"""
|
|
Asset Classification — INVARIANT characteristics only. System-wide asset universe.
|
|
|
|
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,
|
|
|
|
Plus:
|
|
EXCHANGE — multi-exchange support:
|
|
ExchangeProfile (per-exchange metadata), exchange-asset mapping
|
|
(which exchanges trade which assets, with exchange-specific parameters).
|
|
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
|
|
|
|
|
|
# ==============================================================================
|
|
# ExchangeProfile — multi-exchange support
|
|
# ==============================================================================
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ExchangeProfile:
|
|
"""Metadata for a trading venue. System-wide — used by BLUE/VIOLET/UV/MALKHUT."""
|
|
exchange_id: str # canonical key: "binance", "bingx", "bybit", etc.
|
|
display_name: str # human-readable: "Binance", "BingX"
|
|
has_spot: bool
|
|
has_perps: bool
|
|
has_options: bool
|
|
api_base_url: str # REST API root
|
|
ws_base_url: str # WebSocket root ("" if not applicable)
|
|
default_taker_fee_bps: float
|
|
default_maker_fee_bps: float
|
|
typical_latency_ms: float
|
|
|
|
|
|
EXCHANGE_PROFILES: Dict[str, ExchangeProfile] = {}
|
|
|
|
BINANCE = ExchangeProfile(
|
|
exchange_id="binance", display_name="Binance",
|
|
has_spot=True, has_perps=True, has_options=True,
|
|
api_base_url="https://api.binance.com",
|
|
ws_base_url="wss://stream.binance.com:9443",
|
|
default_taker_fee_bps=0.4, default_maker_fee_bps=0.2,
|
|
typical_latency_ms=40,
|
|
)
|
|
EXCHANGE_PROFILES["binance"] = BINANCE
|
|
|
|
BINGX = ExchangeProfile(
|
|
exchange_id="bingx", display_name="BingX",
|
|
has_spot=True, has_perps=True, has_options=False,
|
|
api_base_url="https://open-api.bingx.com",
|
|
ws_base_url="wss://open-api.bingx.com/swapMarket",
|
|
default_taker_fee_bps=0.5, default_maker_fee_bps=0.2,
|
|
typical_latency_ms=100,
|
|
)
|
|
EXCHANGE_PROFILES["bingx"] = BINGX
|
|
|
|
BYBIT = ExchangeProfile(
|
|
exchange_id="bybit", display_name="Bybit",
|
|
has_spot=True, has_perps=True, has_options=True,
|
|
api_base_url="https://api.bybit.com",
|
|
ws_base_url="wss://stream.bybit.com/v5/public/linear",
|
|
default_taker_fee_bps=0.06, default_maker_fee_bps=-0.01,
|
|
typical_latency_ms=50,
|
|
)
|
|
EXCHANGE_PROFILES["bybit"] = BYBIT
|
|
|
|
|
|
def get_exchange(exchange_id: str) -> Optional[ExchangeProfile]:
|
|
return EXCHANGE_PROFILES.get(exchange_id)
|
|
|
|
|
|
def list_exchanges() -> List[str]:
|
|
return list(EXCHANGE_PROFILES.keys())
|
|
|
|
|
|
# ==============================================================================
|
|
# 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
|
|
|
|
# --- Exchange membership (which venues trade this asset) ---
|
|
exchanges: tuple[str, ...] = ("binance",) # tuple of exchange_ids
|
|
|
|
@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,
|
|
exchanges: Sequence[str] = ("binance",),
|
|
) -> 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,
|
|
exchanges=tuple(exchanges),
|
|
)
|
|
|
|
|
|
# ==============================================================================
|
|
# 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]
|
|
|
|
|
|
def get_assets_on_exchange(exchange_id: str) -> List[AssetProfile]:
|
|
"""Assets traded on a given exchange."""
|
|
return [p for p in ASSET_PROFILES.values() if exchange_id in p.exchanges]
|
|
|
|
|
|
def get_common_assets(exchange_a: str, exchange_b: str) -> List[AssetProfile]:
|
|
"""Assets traded on BOTH exchanges (intersection)."""
|
|
return [p for p in ASSET_PROFILES.values()
|
|
if exchange_a in p.exchanges and exchange_b in p.exchanges]
|
|
|
|
|
|
def get_exchange_for_asset(symbol: str) -> tuple[str, ...]:
|
|
"""Return which exchanges trade a given asset."""
|
|
p = ASSET_PROFILES.get(symbol)
|
|
return p.exchanges if p else ()
|