Files
gridbot/config.py.bak.trend.20260616_095523

162 lines
8.1 KiB
Plaintext

"""
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", "1"))
INACTIVITY_PRICE_DRIFT_PCT = float(os.getenv("INACTIVITY_PRICE_DRIFT_PCT", "0.3"))
# === 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"))
# === Local trend detector (slope-based, on price_history) ===
# Independent of Kronos. Linear-regression slope over the last TREND_LOOKBACK
# points in price_history. When slope > TREND_THRESHOLD_PCT% AND R² >
# TREND_MIN_CONFIDENCE — bias="up" (or "down" for negative slope).
# On uptrend: shift buy_anchor up by TREND_UP_ANCHOR_SHIFT to make BUY
# levels sit closer to current price (catch pullbacks in rising trends).
# On downtrend: shift buy_anchor down (skip falling knives).
TREND_ENABLED = os.getenv("TREND_ENABLED", "false").lower() in ("true", "1", "yes")
TREND_LOOKBACK = int(os.getenv("TREND_LOOKBACK", "60"))
TREND_THRESHOLD_PCT = float(os.getenv("TREND_THRESHOLD_PCT", "0.3"))
TREND_MIN_CONFIDENCE = float(os.getenv("TREND_MIN_CONFIDENCE", "0.5"))
TREND_UP_ANCHOR_SHIFT = float(os.getenv("TREND_UP_ANCHOR_SHIFT", "0.0015"))
TREND_DOWN_ANCHOR_SHIFT = float(os.getenv("TREND_DOWN_ANCHOR_SHIFT", "0.0015"))
# === Local trend detector (slope-based, on price_history) ===
# Independent of Kronos. Linear-regression slope over the last TREND_LOOKBACK
# points in price_history. When slope > TREND_THRESHOLD_PCT% AND R² >
# TREND_MIN_CONFIDENCE — bias="up" (or "down" for negative slope).
# On uptrend: shift buy_anchor up by TREND_UP_ANCHOR_SHIFT to make BUY
# levels sit closer to current price (catch pullbacks in rising trends).
# On downtrend: shift buy_anchor down (skip falling knives).
TREND_ENABLED = os.getenv("TREND_ENABLED", "false").lower() in ("true", "1", "yes")
TREND_LOOKBACK = int(os.getenv("TREND_LOOKBACK", "60"))
TREND_THRESHOLD_PCT = float(os.getenv("TREND_THRESHOLD_PCT", "0.3"))
TREND_MIN_CONFIDENCE = float(os.getenv("TREND_MIN_CONFIDENCE", "0.5"))
TREND_UP_ANCHOR_SHIFT = float(os.getenv("TREND_UP_ANCHOR_SHIFT", "0.0015"))
TREND_DOWN_ANCHOR_SHIFT = float(os.getenv("TREND_DOWN_ANCHOR_SHIFT", "0.0015"))
# === Local trend detector (slope-based, on price_history) ===
# Independent of Kronos. Linear-regression slope over the last TREND_LOOKBACK
# points in price_history. When slope > TREND_THRESHOLD_PCT% AND R² >
# TREND_MIN_CONFIDENCE — bias="up" (or "down" for negative slope).
# On uptrend: shift buy_anchor up by TREND_UP_ANCHOR_SHIFT to make BUY
# levels sit closer to current price (catch pullbacks in rising trends).
# On downtrend: shift buy_anchor down (skip falling knives).
TREND_ENABLED = os.getenv("TREND_ENABLED", "false").lower() in ("true", "1", "yes")
TREND_LOOKBACK = int(os.getenv("TREND_LOOKBACK", "60"))
TREND_THRESHOLD_PCT = float(os.getenv("TREND_THRESHOLD_PCT", "0.3"))
TREND_MIN_CONFIDENCE = float(os.getenv("TREND_MIN_CONFIDENCE", "0.5"))
TREND_UP_ANCHOR_SHIFT = float(os.getenv("TREND_UP_ANCHOR_SHIFT", "0.0015"))
TREND_DOWN_ANCHOR_SHIFT = float(os.getenv("TREND_DOWN_ANCHOR_SHIFT", "0.0015"))