Files
gridbot/kronos_advisor_main.py
T

122 lines
4.6 KiB
Python

#!/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()