122 lines
4.6 KiB
Python
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()
|