From 876e1faf2ab671c8f600d68b3738bacd365333d1 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 03:47:13 +0900 Subject: [PATCH] Add live execution guard --- app/live_execution.py | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 app/live_execution.py diff --git a/app/live_execution.py b/app/live_execution.py new file mode 100644 index 0000000..09b9f4c --- /dev/null +++ b/app/live_execution.py @@ -0,0 +1,72 @@ +from dataclasses import dataclass +from typing import Any + +from sqlalchemy.orm import Session + +from app.ai_bot import AiTradingBot +from app.core import Settings +from app.models import AiDecision + + +@dataclass +class ExecutionResult: + executed: bool + mode: str + message: str + payload: dict[str, Any] + + +class LiveExecutionGuard: + def __init__(self, settings: Settings, ai_bot: AiTradingBot) -> None: + self.settings = settings + self.ai_bot = ai_bot + + def assert_live_ready(self, db: Session) -> dict[str, Any]: + state = self.ai_bot.get_or_create_state(db) + problems: list[str] = [] + + if state.trade_mode != 'live': + problems.append('bot state trade_mode is not live') + if not state.live_acknowledged: + problems.append('live trading risk acknowledgement is missing') + if state.emergency_stop: + problems.append('emergency stop is active') + if not self.settings.enable_live_trading or self.settings.trade_mode != 'live': + problems.append('environment live flags are not enabled') + if not self.settings.coinex_credentials_present: + problems.append('CoinEx credentials are missing') + + return { + 'ready': not problems, + 'problems': problems, + 'state': self.ai_bot.state_to_dict(state), + 'env_live_enabled': self.settings.live_enabled, + 'coinex_credentials_present': self.settings.coinex_credentials_present, + } + + def execute_guarded_live_order(self, db: Session, decision: AiDecision, quote_amount: float) -> ExecutionResult: + readiness = self.assert_live_ready(db) + risk = self.ai_bot.risk_check(db, decision, quote_amount) + + if not readiness['ready']: + return ExecutionResult( + executed=False, + mode='live_guard_blocked', + message='Live execution is not ready', + payload={'readiness': readiness, 'risk': risk}, + ) + + if not risk['allowed']: + return ExecutionResult( + executed=False, + mode='live_risk_blocked', + message='Risk engine blocked live execution', + payload={'readiness': readiness, 'risk': risk}, + ) + + return ExecutionResult( + executed=False, + mode='live_adapter_pending', + message='Live guard passed. Low-level CoinEx order adapter is intentionally separated for final verification.', + payload={'readiness': readiness, 'risk': risk, 'decision_id': decision.id}, + )