""" Grid Trading Bot — config. All secrets MUST come from environment (.env), never from defaults in code. """ import os from pathlib import Path from dotenv import load_dotenv # Load .env from project root (cwd or script dir) so all os.getenv() below # see real values, not code defaults. Safe to call multiple times. _ENV_PATH = Path(__file__).resolve().parent / ".env" if _ENV_PATH.exists(): load_dotenv(_ENV_PATH, override=False) else: # fallback: cwd/.env (for 'python main.py' from project dir) load_dotenv(override=False) def _require(name: str) -> str: """Read required env var. Empty/None → raise (only when LIVE mode is on).""" v = os.getenv(name, "").strip() if not v: raise RuntimeError( f"Environment variable {name!r} is empty. " f"Set it in .env (see .env.example). Aborting." ) return v def _opt(name: str, default: str = "") -> str: return os.getenv(name, default).strip() # === Tradernet API credentials === # LIVE: ключи обязательны → main.py вызовет _require(). # DEMO: ключи могут быть пустыми (например, dry-run без сети). TRADERNET_PUBLIC_KEY = os.getenv("TRADERNET_PUBLIC_KEY", "") TRADERNET_PRIVATE_KEY = os.getenv("TRADERNET_PRIVATE_KEY", "") TRADERNET_LOGIN = os.getenv("TRADERNET_LOGIN", "") TRADERNET_PASSWORD = os.getenv("TRADERNET_PASSWORD", "") TRADERNET_BASE_URL = os.getenv("TRADERNET_BASE_URL", "https://tradernet.by") def require_tradernet_keys() -> None: """Вызывайте из main.py в начале LIVE-режима. Падает, если хотя бы один ключ пустой.""" _require("TRADERNET_PUBLIC_KEY") _require("TRADERNET_PRIVATE_KEY") # === Trading settings === SYMBOL = os.getenv("SYMBOL", "BTC-USDT.IMEX") QUOTE = os.getenv("QUOTE", "USDT") BASE = os.getenv("BASE", "BTC") # === Grid settings (default 10 levels = 20 orders total) === GRID_LEVELS = int(os.getenv("GRID_LEVELS", "10")) GRID_STEP_PERCENT = float(os.getenv("GRID_STEP_PERCENT", "0.005")) GRID_TAKE_PROFIT_PERCENT = float(os.getenv("GRID_TAKE_PROFIT_PERCENT", "0.5")) GRID_RENEW_THRESHOLD = int(os.getenv("GRID_RENEW_THRESHOLD", "3")) GRID_PRICE_OFFSET = float(os.getenv("GRID_PRICE_OFFSET", "0")) # === Stale grid recovery === GRID_STALE_DROP_PERCENT = float(os.getenv("GRID_STALE_DROP_PERCENT", "5.0")) # === Inactivity-driven soft rebuild === # Если за INACTIVITY_RESTART_HOURS часов не было ни одной сделки (BUY/SELL fill) # И BTC отошёл от центра сетки (start_price) более чем на # INACTIVITY_PRICE_DRIFT_PCT процентов — отменяем висящие BUY-ордера и # пересобираем сетку вокруг текущей цены (мягкий рестарт: процесс не # перезапускается). Применяется ТОЛЬКО в DEMO-режиме. INACTIVITY_RESTART_HOURS = float(os.getenv("INACTIVITY_RESTART_HOURS", "12")) INACTIVITY_PRICE_DRIFT_PCT = float(os.getenv("INACTIVITY_PRICE_DRIFT_PCT", "0.5")) # === State === STATE_DIR = Path(os.getenv("STATE_DIR", str(Path.home() / ".openclaw"))) STATE_FILE = str(STATE_DIR / "grid-state.json") # === Public server URL === SERVER_URL = os.getenv("SERVER_URL", "https://gridbot.kolp.pro") # === Demo / Live === DEMO_MODE = os.getenv("DEMO_MODE", "true").lower() in ("true", "1", "yes") DEMO_START_BALANCE = float(os.getenv("DEMO_START_BALANCE", "100")) LIVE_TRADING_DRY_RUN = os.getenv("LIVE_TRADING_DRY_RUN", "true").lower() in ("true", "1", "yes") # === Telegram === TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "") TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "") # === Flask === HOST = os.getenv("HOST", "0.0.0.0") PORT = int(os.getenv("PORT", "5555")) DEBUG = os.getenv("DEBUG", "false").lower() in ("true", "1", "yes") # === Web auth (Basic Auth для дашборда /api/*) === # ВАЖНО: обязательно сменить дефолты в .env. Сейчас — deprecation-дефолты, # которые выдаются на 1 запуск, если .env не заполнен. WEB_USERNAME = os.getenv("WEB_USERNAME", "admin") WEB_PASSWORD = os.getenv("WEB_PASSWORD", "changeme") # === Kronos advisor === # KRONOS_ENABLED: подмешивать kronos_advice.json в параметры сетки # KRONOS_LIVE_ENABLED: только в LIVE — реально применять советы к ордерам # KRONOS_MODEL: "kronos-mini" | "kronos-small" (mini быстрее, small точнее) # KRONOS_TF_MIN: таймфрейм свечей (1, 5, 15, 60, 1440) # KRONOS_LOOKBACK: сколько свечей подавать в модель (≤ 512 для small/mini) # KRONOS_PRED_LEN: сколько свечей прогнозировать # KRONOS_MIN_CONFIDENCE: ниже этого — fallback на константы из .env KRONOS_ENABLED = os.getenv("KRONOS_ENABLED", "false").lower() in ("true", "1", "yes") KRONOS_LIVE_ENABLED = os.getenv("KRONOS_LIVE_ENABLED", "false").lower() in ("true", "1", "yes") KRONOS_MODEL = os.getenv("KRONOS_MODEL", "kronos-mini") KRONOS_TF_MIN = int(os.getenv("KRONOS_TF_MIN", "60")) KRONOS_LOOKBACK = int(os.getenv("KRONOS_LOOKBACK", "400")) KRONOS_PRED_LEN = int(os.getenv("KRONOS_PRED_LEN", "24")) KRONOS_MIN_CONFIDENCE = float(os.getenv("KRONOS_MIN_CONFIDENCE", "0.4")) KRONOS_ADVICE_FILE = os.getenv("KRONOS_ADVICE_FILE", str(STATE_DIR / "kronos_advice.json"))