Stage 04.2 - journal and event log

This commit is contained in:
2026-04-16 13:13:03 +03:00
parent c35deeaefa
commit 2c49bb70c0
11 changed files with 780 additions and 28 deletions

View File

@@ -22,11 +22,31 @@ from src.integrations.exchange.models import (
from src.integrations.exchange.private_client import ExchangePrivateClient
from src.integrations.exchange.rest_client import ExchangeRestClient
from src.integrations.exchange.symbol_utils import normalize_symbol, symbol_candidates
from src.trading.journal.service import JournalService
class ExchangeService:
def __init__(self) -> None:
self.settings = load_settings()
self.journal = JournalService()
def _log_info(self, event_type: str, message: str, payload: dict | None = None) -> None:
try:
self.journal.log_info(event_type, message, payload)
except Exception:
pass
def _log_warning(self, event_type: str, message: str, payload: dict | None = None) -> None:
try:
self.journal.log_warning(event_type, message, payload)
except Exception:
pass
def _log_error(self, event_type: str, message: str, payload: dict | None = None) -> None:
try:
self.journal.log_error(event_type, message, payload)
except Exception:
pass
def get_health(self) -> ExchangeHealth:
if not self.settings.exchange_enabled:
@@ -100,14 +120,50 @@ class ExchangeService:
auth_health = self.get_private_auth_health()
if not auth_health.ok:
self._log_error(
"balance_summary_error",
auth_health.message,
{
"exchange_name": self.settings.exchange_name,
"default_symbol": self.settings.default_symbol,
},
)
raise ExchangeError(auth_health.message)
payload = ExchangePrivateClient().get_account_info(show_zero_balance=False)
balances = parse_account_balances(payload)
try:
payload = ExchangePrivateClient().get_account_info(show_zero_balance=False)
balances = parse_account_balances(payload)
except Exception as exc:
self._log_error(
"balance_summary_error",
f"Не удалось получить баланс: {exc}",
{
"exchange_name": self.settings.exchange_name,
"default_symbol": self.settings.default_symbol,
},
)
raise ExchangeError(f"Не удалось получить баланс: {exc}") from exc
if not balances:
self._log_warning(
"balance_summary_empty",
"Баланс получен, но список активов пуст или не распознан.",
{
"exchange_name": self.settings.exchange_name,
"default_symbol": self.settings.default_symbol,
},
)
raise ExchangeError("Баланс получен, но список активов пуст или не распознан.")
self._log_info(
"balance_summary_loaded",
f"Баланс успешно получен. Активов: {len(balances)}",
{
"exchange_name": self.settings.exchange_name,
"assets_count": len(balances),
},
)
return balances
def get_exchange_symbols(self) -> list[ExchangeSymbol]:
@@ -219,13 +275,32 @@ class ExchangeService:
def _get_real_price(self, symbol: str) -> TickerPrice:
client = ExchangeRestClient()
payload = client.get_json(
"/api/v2/ticker/24hr",
params={"symbol": symbol},
)
try:
payload = client.get_json(
"/api/v2/ticker/24hr",
params={"symbol": symbol},
)
except Exception as exc:
self._log_error(
"market_price_error",
f"Не удалось получить цену инструмента {symbol}: {exc}",
{
"symbol": symbol,
"exchange_name": self.settings.exchange_name,
},
)
raise
price_raw = payload.get("lastPrice")
if price_raw is None:
self._log_error(
"market_price_error",
"Field 'lastPrice' is missing in ticker response.",
{
"symbol": symbol,
"exchange_name": self.settings.exchange_name,
},
)
raise ExchangeError("Field 'lastPrice' is missing in ticker response.")
close_time = payload.get("closeTime") or payload.get("eventTime") or ""