from pydantic_settings import BaseSettings class Settings(BaseSettings): database_url: str frontend_url: str = "http://localhost:3001" truth_social_rss: str = "https://truthsocial.com/@realDonaldTrump.rss" truth_social_poll_seconds: int = 15 # WS URL is now built programmatically in binance.py from ASSET_MAP so the # stream list and the routing table are always in sync. This setting is kept # for external callers that still reference it; binance.py ignores it. binance_ws_url: str = "wss://data-stream.binance.vision/stream" # base only binance_rest_url: str = "https://data-api.binance.vision" environment: str = "production" ai_api_key: str = "" ai_base_url: str = "https://api.deepseek.com/v1" ai_model: str = "deepseek-v4-pro" # batch / reanalysis (quality over speed) ai_live_model: str = "deepseek-v4-flash" # live post analysis (latency-sensitive, ~2s) # Native Anthropic API key — if set, takes priority over ai_api_key + ai_base_url. # Get from https://console.anthropic.com → API Keys anthropic_api_key: str = "" # Hyperliquid — API wallet private key (NOT your MetaMask key) # Created at https://app.hyperliquid.xyz/API and authorized by MetaMask hl_api_private_key: str = "" # Your MetaMask address — this is the actual account holding USDC/positions hl_account_address: str = "" # BTC perp leverage (1–50x isolated margin) hl_leverage: int = 3 # Trade mainnet (True) or testnet (False) hl_mainnet: bool = True # KEK for envelope-encrypting HL API keys at rest. # Generate with: openssl rand -hex 32 encryption_key: str = "" # Shared secret for /api/signals/ingest — external trading modules pass it # in the X-Ingest-Key header. Empty (default) = ingest endpoint is REJECTED # entirely until a key is configured (fail-closed). ingest_api_key: str = "" # Base URL for internal signal ingest (used by scanners). In Docker, set to # the service name (e.g. http://api:8000); bare-metal defaults to localhost. ingest_base_url: str = "http://localhost:8000" # Glassnode on-chain data. Used only by the BTC bottom-reversal state # machine (MVRV-Z + STH-SOPR). Empty = scanner logs and skips fail-closed. glassnode_api_key: str = "" # Etherscan API key — free at etherscan.io/register → My API Keys. # Used for KOL A-tier: fetch all ERC-20 token balances for a given ETH address. # Empty = skip Ethereum wallet snapshots (only HL perp positions polled). etherscan_api_key: str = "" # ── Telegram push alerts ───────────────────────────────────────────────── # Bot token from @BotFather (https://t.me/BotFather → /newbot). Free. # Empty (default) = Telegram alerts disabled completely (bot loop skipped, # notify_signal becomes a no-op, API endpoints return 503). telegram_bot_token: str = "" # Bot username (no @) — used by the Settings UI to render the deep link # t.me/?start=. Example: "trumpalpha_bot". telegram_bot_username: str = "" # Public broadcast channel chat_id (e.g. "-1001234567890" or "@trumpalpha"). # When set, every actionable signal is also posted here in sanitised form # (no trading details, no /adopt CTA). Leave empty to disable. telegram_public_channel_id: str = "" # ── X (Twitter) auto-poster ────────────────────────────────────────────── # OAuth 1.0a User Context — for posting from a single fixed bot account # (@TrumpAlpha_signals). Get all four from developer.x.com → your App → # "Keys and tokens". App permissions MUST be "Read and Write", and the # Access Token/Secret must be regenerated AFTER setting write permission. # Bearer Token / Client ID / Client Secret are NOT used (those are read / # OAuth2 web-flow credentials). Empty = X posting disabled (no-op). x_api_key: str = "" # = Consumer Key x_api_secret: str = "" # = Consumer Secret x_access_token: str = "" x_access_secret: str = "" # Master switch. Even with keys set, posting stays off unless this is true. # Lets you load creds without going live (dry-run = logs the tweet text). x_enabled: bool = False # Only post when ai_confidence >= this (0–100). Keeps low-quality noise off # the public timeline. Scanner signals carry their own score. x_min_confidence: int = 60 # Hard daily cap on tweets (initial + follow-ups counted together) to stay # under Free-tier 1,500/mo and avoid spam flags. In-memory, resets at UTC # midnight. Single-process by design so a plain counter is safe. x_daily_cap: int = 40 # Minutes after the initial prediction tweet to post the result follow-up. x_followup_minutes: int = 15 # Public site URL appended to the follow-up tweet. Falls back to frontend_url. x_link_url: str = "" model_config = {"env_file": ".env", "env_file_encoding": "utf-8", "extra": "ignore"} settings = Settings()