#!/usr/bin/env python3 """ CLI: запустить Kronos advisor и записать kronos_advice.json. Использование (demo, офлайн): python kronos_advisor_main.py --source binance --lookback 400 --pred-len 24 --tf 60 Использование (live, prod): python kronos_advisor_main.py --source tradernet --live В режиме --live используется конфиг из config.py (читает .env) и пишет kronos_advice.json в STATE_DIR, который main.py бота потом подхватывает. """ from __future__ import annotations import argparse import json import logging import os import sys from pathlib import Path # Делаем grid-bot/ корнем HERE = Path(__file__).parent.resolve() # Добавляем kronos/ в sys.path, чтобы model/ (исходники Kronos) находился if str(HERE / "kronos") not in sys.path: sys.path.insert(0, str(HERE / "kronos")) sys.path.insert(0, str(HERE)) # Kronos-репозиторий — соседний каталог (~/projects/Kronos) KRONOS_DIR = (HERE / ".." / "Kronos").resolve() if KRONOS_DIR.exists(): sys.path.insert(0, str(KRONOS_DIR)) def main(): ap = argparse.ArgumentParser() ap.add_argument("--source", choices=["binance", "tradernet"], default="binance") ap.add_argument("--symbol", default="BTC-USDT.IMEX", help="Tradernet-символ (например BTC-USDT.IMEX)") ap.add_argument("--tf", type=int, default=60, help="timeframe, минуты (1,5,15,60,1440)") ap.add_argument("--lookback", type=int, default=400) ap.add_argument("--pred-len", type=int, default=24) ap.add_argument("--model", default="kronos-mini", choices=["kronos-mini", "kronos-small"]) ap.add_argument("--device", default="cpu") ap.add_argument("--out", default=None, help="путь к kronos_advice.json (default: STATE_DIR/kronos_advice.json)") ap.add_argument("--live", action="store_true", help="live-режим: читает .env, пишет в STATE_DIR") ap.add_argument("--verbose", "-v", action="store_true") args = ap.parse_args() logging.basicConfig( level=logging.DEBUG if args.verbose else logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) log = logging.getLogger("kronos_advisor_main") # В live-режиме — конфиг из .env if args.live: from dotenv import load_dotenv load_dotenv(HERE / ".env") import config symbol = config.SYMBOL tf = config.KRONOS_TF_MIN lookback = config.KRONOS_LOOKBACK pred_len = config.KRONOS_PRED_LEN model_name = config.KRONOS_MODEL out_path = Path(config.KRONOS_ADVICE_FILE) # В live идём через Tradernet src_kind = "tradernet" else: symbol = args.symbol tf = args.tf lookback = args.lookback pred_len = args.pred_len model_name = args.model out_path = Path(args.out) if args.out else HERE / "kronos_advice.json" src_kind = args.source log.info("source=%s symbol=%s tf=%d lookback=%d pred_len=%d model=%s", src_kind, symbol, tf, lookback, pred_len, model_name) # 1) Источник from kronos import BinancePublicSource, TradernetHlocSource, KronosAdvisor if src_kind == "binance": try: src = BinancePublicSource.from_tradernet(symbol, tf) log.info("Binance symbol: %s", src.symbol) except ValueError as e: log.error("Binance mapping failed: %s. Используй --source tradernet", e) sys.exit(2) else: import config as _cfg from api import TradernetAPI api = TradernetAPI( public_key=_cfg.TRADERNET_PUBLIC_KEY, private_key=_cfg.TRADERNET_PRIVATE_KEY, login=_cfg.TRADERNET_LOGIN, password=_cfg.TRADERNET_PASSWORD, base_url=_cfg.TRADERNET_BASE_URL, ) src = TradernetHlocSource(api, symbol, tf) # 2) Тянем свечи log.info("Fetching OHLCV ...") df = src.fetch_ohlcv(lookback=lookback) log.info("Got %d candles, last close=%.2f", len(df), float(df["close"].iloc[-1])) # 3) Советник advisor = KronosAdvisor(model_name=model_name, device=args.device) advice = advisor.advise(df, lookback=lookback, pred_len=pred_len, tf_min=tf) log.info("Advice: %s", advice.to_json_safe()) # 4) Записываем out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(advice.to_json_safe()) log.info("Wrote %s", out_path) if __name__ == "__main__": main()