412 lines
16 KiB
Python
412 lines
16 KiB
Python
"""System configuration defaults and DB-backed accessors."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
|
||
from app.db.runtime_config_db import (
|
||
get_bootstrap_admin_config,
|
||
get_email_config,
|
||
get_event_driven_config,
|
||
get_llm_config,
|
||
get_monitoring_config,
|
||
get_notification_config,
|
||
get_onchain_config,
|
||
get_paper_trading_config,
|
||
get_price_streamer_config,
|
||
get_scheduler_config,
|
||
get_sentiment_config,
|
||
seed_system_defaults,
|
||
)
|
||
|
||
|
||
def _env_bool(name, default=False):
|
||
value = os.getenv(name)
|
||
if value is None:
|
||
return default
|
||
return str(value).strip().lower() in ("1", "true", "yes", "on")
|
||
|
||
|
||
def _env_int(name, default):
|
||
try:
|
||
return int(os.getenv(name, str(default)) or default)
|
||
except Exception:
|
||
return default
|
||
|
||
|
||
def _env_float(name, default):
|
||
try:
|
||
return float(os.getenv(name, str(default)) or default)
|
||
except Exception:
|
||
return default
|
||
|
||
|
||
def _env_str(name, default=""):
|
||
return os.getenv(name, default).strip()
|
||
|
||
|
||
def _env_list(name, default):
|
||
raw = os.getenv(name, ",".join(default))
|
||
return [x.strip().lower() for x in raw.split(",") if x.strip()] or list(default)
|
||
|
||
|
||
def default_llm_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_LLM_ENABLED", False),
|
||
"base_url": _env_str("ALPHAX_LLM_BASE_URL", "https://api.openai.com/v1"),
|
||
"api_key_env": _env_str("ALPHAX_LLM_API_KEY_ENV", "ALPHAX_LLM_API_KEY"),
|
||
"model": _env_str("ALPHAX_LLM_MODEL", "gpt-4o-mini"),
|
||
"timeout": _env_int("ALPHAX_LLM_TIMEOUT", 20),
|
||
"max_tokens": _env_int("ALPHAX_LLM_MAX_TOKENS", 900),
|
||
"modules": {
|
||
"recommendations": _env_bool("ALPHAX_LLM_RECOMMENDATIONS_ENABLED", True),
|
||
"sentiment": _env_bool("ALPHAX_LLM_SENTIMENT_ENABLED", True),
|
||
"review": _env_bool("ALPHAX_LLM_REVIEW_ENABLED", True),
|
||
},
|
||
}
|
||
|
||
|
||
def default_onchain_config(default_chains=("ethereum", "bsc", "base", "arbitrum", "solana")):
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_ONCHAIN_ENABLED", False),
|
||
"chains": _env_list("ALPHAX_ONCHAIN_CHAINS", default_chains),
|
||
"timeout": _env_int("ALPHAX_ONCHAIN_TIMEOUT", 15),
|
||
"candidate_enabled": _env_bool("ALPHAX_ONCHAIN_CANDIDATE_ENABLED", True),
|
||
"candidate_min_score": _env_float("ALPHAX_ONCHAIN_CANDIDATE_MIN_SCORE", 70),
|
||
"candidate_min_confidence": _env_int("ALPHAX_ONCHAIN_CANDIDATE_MIN_CONFIDENCE", 70),
|
||
"candidate_cooldown_hours": _env_float("ALPHAX_ONCHAIN_CANDIDATE_COOLDOWN_HOURS", 6),
|
||
"dexscreener_enabled": _env_bool("ALPHAX_ONCHAIN_DEXSCREENER_ENABLED", True),
|
||
"dex_volume_spike_pct": _env_float("ALPHAX_ONCHAIN_DEX_VOLUME_SPIKE_PCT", 80),
|
||
"dex_min_liquidity_usd": _env_float("ALPHAX_ONCHAIN_DEX_MIN_LIQUIDITY_USD", 100000),
|
||
"dex_min_volume_24h_usd": _env_float("ALPHAX_ONCHAIN_DEX_MIN_VOLUME_24H_USD", 100000),
|
||
"liquidity_add_pct": _env_float("ALPHAX_ONCHAIN_LIQUIDITY_ADD_PCT", 25),
|
||
"liquidity_remove_pct": _env_float("ALPHAX_ONCHAIN_LIQUIDITY_REMOVE_PCT", -25),
|
||
"whale_tx_usd": _env_float("ALPHAX_ONCHAIN_WHALE_TX_USD", 250000),
|
||
"etherscan_api_key_env": "ALPHAX_ETHERSCAN_API_KEY",
|
||
"helius_api_key_env": "ALPHAX_HELIUS_API_KEY",
|
||
}
|
||
|
||
|
||
def default_paper_trading_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_PAPER_TRADING_ENABLED", True),
|
||
"account_equity_usdt": _env_float("ALPHAX_PAPER_ACCOUNT_EQUITY_USDT", 20000),
|
||
"trade_notional_usdt": _env_float("ALPHAX_PAPER_TRADE_NOTIONAL_USDT", 5000),
|
||
"trade_leverage": _env_float("ALPHAX_PAPER_TRADE_LEVERAGE", 5),
|
||
"fee_rate": _env_float("ALPHAX_PAPER_TRADE_FEE_RATE", 0.001),
|
||
"slippage_pct": _env_float("ALPHAX_PAPER_TRADE_SLIPPAGE_PCT", 0.05),
|
||
}
|
||
|
||
|
||
def default_price_streamer_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_PRICE_STREAMER_ENABLED", True),
|
||
"provider": _env_str("ALPHAX_PRICE_STREAMER_PROVIDER", "binance_spot"),
|
||
"stream_url": _env_str("ALPHAX_PRICE_STREAMER_URL", "wss://stream.binance.com:9443/stream"),
|
||
"refresh_symbols_seconds": _env_float("ALPHAX_PRICE_STREAMER_REFRESH_SYMBOLS_SECONDS", 20),
|
||
"idle_sleep_seconds": _env_float("ALPHAX_PRICE_STREAMER_IDLE_SLEEP_SECONDS", 5),
|
||
"reconnect_delay_seconds": _env_float("ALPHAX_PRICE_STREAMER_RECONNECT_DELAY_SECONDS", 5),
|
||
"max_stream_symbols": _env_int("ALPHAX_PRICE_STREAMER_MAX_SYMBOLS", 200),
|
||
"include_actionable_recommendations": True,
|
||
"include_open_paper_trades": True,
|
||
"update_latest_price_cache": True,
|
||
"sync_paper_trading": True,
|
||
"log_every_events": _env_int("ALPHAX_PRICE_STREAMER_LOG_EVERY_EVENTS", 100),
|
||
}
|
||
|
||
|
||
def default_sentiment_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_SENTIMENT_ENABLED", True),
|
||
"provider": _env_str("ALPHAX_SENTIMENT_PROVIDER", "coingecko"),
|
||
"max_bonus": _env_float("ALPHAX_SENTIMENT_MAX_BONUS", 2),
|
||
"trending_top5_bonus": _env_float("ALPHAX_SENTIMENT_TOP5_BONUS", 2.0),
|
||
"trending_top10_bonus": _env_float("ALPHAX_SENTIMENT_TOP10_BONUS", 1.0),
|
||
"new_entry_bonus": _env_float("ALPHAX_SENTIMENT_NEW_ENTRY_BONUS", 1.0),
|
||
"decay_hours": _env_float("ALPHAX_SENTIMENT_DECAY_HOURS", 6),
|
||
"decay_factor": _env_float("ALPHAX_SENTIMENT_DECAY_FACTOR", 0.1),
|
||
"min_decay": _env_float("ALPHAX_SENTIMENT_MIN_DECAY", 0.3),
|
||
"collect_interval_min": _env_int("ALPHAX_SENTIMENT_COLLECT_INTERVAL_MIN", 30),
|
||
"alert_conditions": {
|
||
"holding_top3": True,
|
||
"new_trending_top10": True,
|
||
},
|
||
}
|
||
|
||
|
||
def default_event_driven_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_EVENT_DRIVEN_ENABLED", True),
|
||
"poll_interval_min": _env_int("ALPHAX_EVENT_POLL_INTERVAL_MIN", 1),
|
||
"decision_target_seconds": _env_int("ALPHAX_EVENT_DECISION_TARGET_SECONDS", 60),
|
||
"news_time_window_hours": _env_float("ALPHAX_EVENT_NEWS_TIME_WINDOW_HOURS", 3),
|
||
"max_event_age_hours": _env_float("ALPHAX_EVENT_MAX_EVENT_AGE_HOURS", 6),
|
||
"dedup_window_hours": _env_float("ALPHAX_EVENT_DEDUP_WINDOW_HOURS", 24),
|
||
"min_importance_level": _env_str("ALPHAX_EVENT_MIN_IMPORTANCE_LEVEL", "A"),
|
||
"sources": {},
|
||
"importance": {
|
||
"s_keywords": [
|
||
"will list",
|
||
"will launch",
|
||
"futures will launch",
|
||
"perpetual contract",
|
||
"launchpool",
|
||
"megadrop",
|
||
"hodler airdrops",
|
||
"coinbase will add",
|
||
"upbit listing",
|
||
"bithumb listing",
|
||
],
|
||
"a_keywords": [
|
||
"margin will add",
|
||
"new trading pairs",
|
||
"earn",
|
||
"convert",
|
||
"roadmap",
|
||
"mainnet",
|
||
"tokenomics",
|
||
"airdrop",
|
||
"burn",
|
||
"buyback",
|
||
"partnership",
|
||
"integration",
|
||
"upgrade",
|
||
],
|
||
"negative_keywords": [
|
||
"delist",
|
||
"suspend trading",
|
||
"remove",
|
||
"cease trading",
|
||
"risk warning",
|
||
],
|
||
},
|
||
"technical_check": {
|
||
"min_tech_score_recommend": 6,
|
||
"min_tech_score_observe": 3,
|
||
"reject_if_24h_gain_gt": 30,
|
||
"warn_if_24h_gain_gt": 18,
|
||
"reject_if_funding_gt": 0.003,
|
||
"allow_static_accumulation": True,
|
||
"allow_volume_breakout": True,
|
||
"allow_ignition": True,
|
||
},
|
||
"push": {
|
||
"recommend": True,
|
||
"observe": True,
|
||
"risk": True,
|
||
"cooldown_hours": 6,
|
||
},
|
||
"theme_expansion": {
|
||
"enabled": True,
|
||
"min_theme_importance": "A",
|
||
"max_expanded_symbols": 12,
|
||
"static_accumulation_bonus": {
|
||
"enabled": True,
|
||
"min_static_count": 8,
|
||
"score_bonus": 3,
|
||
"note": "重大生态事件命中后,强静K蓄力币提前升权",
|
||
},
|
||
"themes": {},
|
||
},
|
||
}
|
||
|
||
|
||
def default_monitoring_config():
|
||
return {
|
||
"untouched_rate": {
|
||
"description": "未触达率自动监控",
|
||
"threshold_pct": 35,
|
||
"check_window_days": 2,
|
||
"auto_bump": {
|
||
"enabled": True,
|
||
"min_score_5_to_6": True,
|
||
"min_score_max": 6,
|
||
"require_human_if_exceeded": True,
|
||
},
|
||
},
|
||
"param_audit": {
|
||
"description": "参数变更审计",
|
||
"validate_script": "scripts/validate_params.py",
|
||
"hash_algorithm": "semantic_sha256",
|
||
"critical_sections": ["confirm", "screener", "pa_engine", "signal_weights", "tracker", "sentiment"],
|
||
},
|
||
}
|
||
|
||
|
||
def default_notification_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_NOTIFICATION_ENABLED", True),
|
||
"feishu": {
|
||
"enabled": _env_bool("ALPHAX_FEISHU_ENABLED", True),
|
||
"webhook_env": _env_str("ALPHAX_FEISHU_WEBHOOK_ENV", "ALTCOIN_FEISHU_WEBHOOK"),
|
||
"timeout": _env_int("ALPHAX_FEISHU_TIMEOUT", 10),
|
||
},
|
||
}
|
||
|
||
|
||
def default_email_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_EMAIL_ENABLED", True),
|
||
"smtp": {
|
||
"host": _env_str("ALPHAX_SMTP_HOST", _env_str("ASTOCK_SMTP_HOST", "")),
|
||
"port": _env_int("ALPHAX_SMTP_PORT", _env_int("ASTOCK_SMTP_PORT", 465)),
|
||
"username_env": _env_str("ALPHAX_SMTP_USERNAME_ENV", "ASTOCK_SMTP_USERNAME"),
|
||
"password_env": _env_str("ALPHAX_SMTP_PASSWORD_ENV", "ASTOCK_SMTP_PASSWORD"),
|
||
"sender": _env_str("ALPHAX_SMTP_SENDER", ""),
|
||
"sender_env": _env_str("ALPHAX_SMTP_SENDER_ENV", "ASTOCK_SMTP_SENDER"),
|
||
"timeout": _env_int("ALPHAX_SMTP_TIMEOUT", 12),
|
||
},
|
||
}
|
||
|
||
|
||
def default_bootstrap_admin_config():
|
||
return {
|
||
"enabled": _env_bool("ALPHAX_BOOTSTRAP_ADMIN", True),
|
||
"email_env": _env_str("ALPHAX_DEFAULT_ADMIN_EMAIL_ENV", "ALPHAX_DEFAULT_ADMIN_EMAIL"),
|
||
"password_env": _env_str("ALPHAX_DEFAULT_ADMIN_PASSWORD_ENV", "ALPHAX_DEFAULT_ADMIN_PASSWORD"),
|
||
}
|
||
|
||
|
||
def default_scheduler_config():
|
||
return {
|
||
"dry_run": _env_bool("ALPHAX_SCHEDULER_DRY_RUN", True),
|
||
"poll_seconds": _env_float("ALPHAX_SCHEDULER_POLL_SECONDS", 1.0),
|
||
"config_reload_seconds": _env_float("ALPHAX_SCHEDULER_CONFIG_RELOAD_SECONDS", 5.0),
|
||
"pending_warn_seconds": _env_float("ALPHAX_SCHEDULER_PENDING_WARN_SECONDS", 30.0),
|
||
}
|
||
|
||
|
||
def seed_runtime_system_defaults():
|
||
return seed_system_defaults({
|
||
"llm": (default_llm_config(), "LLM provider and module switches; API key remains in env"),
|
||
"onchain": (default_onchain_config(), "On-chain provider and signal thresholds; API keys remain in env"),
|
||
"paper_trading": (default_paper_trading_config(), "Paper trading account and execution model"),
|
||
"price_streamer": (default_price_streamer_config(), "Realtime websocket price streamer settings"),
|
||
"sentiment": (default_sentiment_config(), "Sentiment monitoring settings"),
|
||
"event_driven": (default_event_driven_config(), "Event/news driven screening settings"),
|
||
"monitoring": (default_monitoring_config(), "Monitoring and audit settings"),
|
||
"notification": (default_notification_config(), "Notification channel switches and env pointers"),
|
||
"email": (default_email_config(), "SMTP email settings; password remains in env"),
|
||
"bootstrap_admin": (default_bootstrap_admin_config(), "Default admin bootstrap env pointers"),
|
||
"scheduler": (default_scheduler_config(), "Scheduler runtime process settings"),
|
||
})
|
||
|
||
|
||
def _seed_one(key: str, value, description: str):
|
||
return seed_system_defaults({key: (value, description)})
|
||
|
||
|
||
def llm_config():
|
||
cfg = get_llm_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("llm", default_llm_config(), "LLM provider and module switches; API key remains in env")
|
||
cfg = get_llm_config(default=None)
|
||
return cfg or default_llm_config()
|
||
|
||
|
||
def onchain_config(default_chains=("ethereum", "bsc", "base", "arbitrum", "solana")):
|
||
cfg = get_onchain_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("onchain", default_onchain_config(default_chains), "On-chain provider and signal thresholds; API keys remain in env")
|
||
cfg = get_onchain_config(default=None)
|
||
return cfg or default_onchain_config(default_chains)
|
||
|
||
|
||
def paper_trading_config():
|
||
cfg = get_paper_trading_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("paper_trading", default_paper_trading_config(), "Paper trading account and execution model")
|
||
cfg = get_paper_trading_config(default=None)
|
||
return cfg or default_paper_trading_config()
|
||
|
||
|
||
def price_streamer_config():
|
||
cfg = get_price_streamer_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("price_streamer", default_price_streamer_config(), "Realtime websocket price streamer settings")
|
||
cfg = get_price_streamer_config(default=None)
|
||
return cfg or default_price_streamer_config()
|
||
|
||
|
||
def notification_config():
|
||
cfg = get_notification_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("notification", default_notification_config(), "Notification channel switches and env pointers")
|
||
cfg = get_notification_config(default=None)
|
||
return cfg or default_notification_config()
|
||
|
||
|
||
def sentiment_config():
|
||
cfg = get_sentiment_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("sentiment", default_sentiment_config(), "Sentiment monitoring settings")
|
||
cfg = get_sentiment_config(default=None)
|
||
return cfg or default_sentiment_config()
|
||
|
||
|
||
def event_driven_config():
|
||
cfg = get_event_driven_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("event_driven", default_event_driven_config(), "Event/news driven screening settings")
|
||
cfg = get_event_driven_config(default=None)
|
||
return cfg or default_event_driven_config()
|
||
|
||
|
||
def monitoring_config():
|
||
cfg = get_monitoring_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("monitoring", default_monitoring_config(), "Monitoring and audit settings")
|
||
cfg = get_monitoring_config(default=None)
|
||
return cfg or default_monitoring_config()
|
||
|
||
|
||
def email_config():
|
||
cfg = get_email_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("email", default_email_config(), "SMTP email settings; password remains in env")
|
||
cfg = get_email_config(default=None)
|
||
return cfg or default_email_config()
|
||
|
||
|
||
def bootstrap_admin_config():
|
||
cfg = get_bootstrap_admin_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("bootstrap_admin", default_bootstrap_admin_config(), "Default admin bootstrap env pointers")
|
||
cfg = get_bootstrap_admin_config(default=None)
|
||
return cfg or default_bootstrap_admin_config()
|
||
|
||
|
||
def scheduler_config():
|
||
cfg = get_scheduler_config(default=None)
|
||
if cfg is None:
|
||
_seed_one("scheduler", default_scheduler_config(), "Scheduler runtime process settings")
|
||
cfg = get_scheduler_config(default=None)
|
||
return cfg or default_scheduler_config()
|
||
|
||
|
||
__all__ = [
|
||
"bootstrap_admin_config",
|
||
"default_bootstrap_admin_config",
|
||
"default_email_config",
|
||
"default_event_driven_config",
|
||
"default_llm_config",
|
||
"default_monitoring_config",
|
||
"default_notification_config",
|
||
"default_onchain_config",
|
||
"default_paper_trading_config",
|
||
"default_price_streamer_config",
|
||
"default_scheduler_config",
|
||
"default_sentiment_config",
|
||
"email_config",
|
||
"event_driven_config",
|
||
"llm_config",
|
||
"monitoring_config",
|
||
"notification_config",
|
||
"onchain_config",
|
||
"paper_trading_config",
|
||
"price_streamer_config",
|
||
"scheduler_config",
|
||
"sentiment_config",
|
||
"seed_runtime_system_defaults",
|
||
]
|