From 1bb22379b65a1f6edd800c7e045b1fa73ce2f96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= <78488229+viktor138irk@users.noreply.github.com> Date: Fri, 8 May 2026 04:25:34 +0900 Subject: [PATCH] Autostart monitor and expose human-readable bot logs --- app/main.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/main.py b/app/main.py index cd94454..7fc92a0 100644 --- a/app/main.py +++ b/app/main.py @@ -29,11 +29,20 @@ static_dir = Path(__file__).parent / 'static' app.mount('/static', StaticFiles(directory=static_dir), name='static') +def ensure_monitor_running() -> None: + global monitor_task + if monitor_task is None or monitor_task.done(): + monitor_task = asyncio.create_task(monitor.loop(SessionLocal, interval_seconds=10)) + + @app.on_event('startup') -def on_startup() -> None: +async def on_startup() -> None: init_db() with SessionLocal() as db: legacy_bot.ensure_defaults(db) + state = ai_bot.get_or_create_state(db) + if state.enabled: + ensure_monitor_running() @app.get('/') @@ -97,6 +106,8 @@ async def update_bot_settings(enabled: bool | None = None, trade_mode: str | Non if trade_mode is not None and trade_mode not in {'demo', 'live'}: raise HTTPException(status_code=400, detail='trade_mode must be demo or live') state = ai_bot.update_state(db, enabled=enabled, trade_mode=trade_mode, trade_style_mode=trade_style_mode, min_signal_score=min_signal_score, max_open_positions=max_open_positions, max_quote_per_trade=max_quote_per_trade, emergency_stop=emergency_stop) + if state.enabled: + ensure_monitor_running() return ai_bot.state_to_dict(state) @@ -105,12 +116,15 @@ async def monitor_status(_: str = Depends(require_auth)) -> dict: return monitor.status() +@app.get('/api/v1/monitor/logs') +async def monitor_logs(limit: int = 80, _: str = Depends(require_auth), db: Session = Depends(get_db)) -> dict: + return {'items': monitor.recent_logs(db, limit)} + + @app.post('/api/v1/monitor/start') async def monitor_start(_: str = Depends(require_auth), db: Session = Depends(get_db)) -> dict: - global monitor_task state = ai_bot.update_state(db, enabled=True) - if monitor_task is None or monitor_task.done(): - monitor_task = asyncio.create_task(monitor.loop(SessionLocal, interval_seconds=20)) + ensure_monitor_running() data = monitor.status() data['bot'] = ai_bot.state_to_dict(state) return data @@ -175,6 +189,7 @@ def _force_demo_trade(db: Session, decision) -> dict: db.add(decision) db.commit() legacy_bot.snapshot_history(db) + monitor.write_log(db, 'success', 'manual_auto_order', f'Открыта сделка вручную: {trade.side.upper()} {trade.market}, сумма {trade.quote_amount:.2f} USDT, цена {trade.price:.8f}.', market=trade.market, action=trade.side, score=decision.score) return {'executed': True, 'mode': 'demo', 'decision': decision_to_dict(decision), 'trade': trade.model_dump(mode='json')} @@ -192,6 +207,7 @@ async def auto_trade(market: str | None = None, _: str = Depends(require_auth), state = ai_bot.get_or_create_state(db) decision = await ai_bot.make_decision(db, market) if state.trade_mode == 'live': + monitor.write_log(db, 'warning', 'live_order_pending', f'Live-режим включен. Сигнал {decision.market} найден, но CoinEx live adapter еще не подключен.', market=decision.market, action=decision.action, score=decision.score) return {'executed': False, 'mode': 'live', 'decision': decision_to_dict(decision), 'message': 'Live mode is selected. CoinEx execution adapter is the next implementation step.'} return _force_demo_trade(db, decision)