Phase 4 hardening: status CLI, lock file, backup job, hash enforce, pooling, real bias
Sei interventi mirati sui rischi operativi rilevati nell'audit post-Fase 4. 317 test pass, mypy strict pulito, ruff clean. 1. status CLI: legge SQLite reale e mostra kill_switch, posizioni aperte, environment, config_version, last_health_check, started_at. Sostituisce il placeholder "phase 0 skeleton". 2. Lock file single-instance: runtime/lockfile.py acquisisce data/.lockfile via fcntl.flock al boot di run_forever; un secondo container fallisce subito con LockError. 3. Backup orario nello scheduler: nuovo job APScheduler 0 * * * * chiama scripts.backup.backup_database + prune_backups. 4. config_hash enforce su start: il CLI start verifica l'integrità del file (enforce_hash=True). Mismatch → exit 1 prima di toccare stato. dry-run resta enforce_hash=False per debug. 5. Connection pooling MCP: RuntimeContext espone un httpx.AsyncClient long-lived condiviso da tutti i wrapper (limits 20/10 connections/keepalive). aclose() chiamato in run_forever finale. 6. Bias direzionale reale: deribit.historical_close + deribit.adx_14 popolano TrendContext con spot a 30 giorni e ADX(14) effettivi. Sblocca bull_put e bear_call. Quando i dati storici mancano l'engine emette alert MEDIUM e cade su no_entry in modo deterministico. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,8 @@ from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
|
||||
from cerbero_bite.clients._base import HttpToolClient
|
||||
from cerbero_bite.clients.deribit import DeribitClient
|
||||
from cerbero_bite.clients.hyperliquid import HyperliquidClient
|
||||
@@ -56,8 +58,14 @@ class RuntimeContext:
|
||||
portfolio: PortfolioClient
|
||||
telegram: TelegramClient
|
||||
|
||||
http_client: httpx.AsyncClient
|
||||
|
||||
clock: Callable[[], datetime]
|
||||
|
||||
async def aclose(self) -> None:
|
||||
"""Close the shared HTTP client. Idempotent."""
|
||||
await self.http_client.aclose()
|
||||
|
||||
|
||||
def _utc_now() -> datetime:
|
||||
return datetime.now(UTC)
|
||||
@@ -103,6 +111,14 @@ def build_runtime(
|
||||
clock=clk,
|
||||
)
|
||||
|
||||
# Single long-lived AsyncClient shared by every wrapper. httpx pools
|
||||
# connections per host so the snapshot stage of the entry cycle
|
||||
# avoids paying TLS/TCP handshakes on each call.
|
||||
http_client = httpx.AsyncClient(
|
||||
timeout=httpx.Timeout(timeout_s),
|
||||
limits=httpx.Limits(max_connections=20, max_keepalive_connections=10),
|
||||
)
|
||||
|
||||
def _client(service: str) -> HttpToolClient:
|
||||
return HttpToolClient(
|
||||
service=service,
|
||||
@@ -110,6 +126,7 @@ def build_runtime(
|
||||
token=token,
|
||||
timeout_s=timeout_s,
|
||||
retry_max=retry_max,
|
||||
client=http_client,
|
||||
)
|
||||
|
||||
telegram = TelegramClient(_client("telegram"))
|
||||
@@ -131,5 +148,6 @@ def build_runtime(
|
||||
hyperliquid=HyperliquidClient(_client("hyperliquid")),
|
||||
portfolio=PortfolioClient(_client("portfolio")),
|
||||
telegram=telegram,
|
||||
http_client=http_client,
|
||||
clock=clk,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user