diff --git a/docs/architecture/arquitetura.md b/docs/architecture/arquitetura.md
new file mode 100644
index 0000000..ba16233
--- /dev/null
+++ b/docs/architecture/arquitetura.md
@@ -0,0 +1,665 @@
+# Arquitetura — Criptotrade (visão completa deste repositório)
+
+> Conjunto completo de **diagramas de arquitetura** de **todo este repositório**, no modelo **C4** (Contexto → Contêineres → Componentes) + vistas de apoio (dados/ERD, fluxo de dados, runtime, implantação, CI/CD, observabilidade, segurança) e **recomendações de arquitetura** com um estado-alvo proposto.
+>
+> Complementa `docs/uml/arquitetura-uml.md` (que detalha as **classes**). Aqui o foco é o **nível arquitetural**: sistemas, contêineres, integrações, dados, deploy e evolução.
+>
+> **Composição do repositório:** Python (backend, ~168 arq.), React/JSX (console em `docs/design/pages/`), 1 arquivo TypeScript gerado (`openapi.d.ts`, o contrato), infra (Docker/nginx/Prometheus/Grafana). Sem Rust.
+
+## Índice
+1. [C4 L1 — Contexto de Sistema](#1-c4-nível-1--contexto-de-sistema)
+2. [C4 L2 — Contêineres](#2-c4-nível-2--contêineres)
+3. [C4 L3 — Componentes: API](#3-c4-nível-3--componentes-do-contêiner-api)
+4. [C4 L3 — Componentes: Loop Orquestrador](#4-c4-nível-3--componentes-do-contêiner-loop-orquestrador)
+5. [Arquitetura de Dados (ERD + stores)](#5-arquitetura-de-dados)
+6. [Fluxo de Dados (DFD)](#6-fluxo-de-dados-dfd)
+7. [Vista de Runtime / Processos](#7-vista-de-runtime--processos)
+8. [Implantação (3 topologias)](#8-implantação)
+9. [Pipeline CI/CD](#9-pipeline-cicd)
+10. [Arquitetura de Observabilidade](#10-arquitetura-de-observabilidade)
+11. [Arquitetura de Segurança](#11-arquitetura-de-segurança)
+12. [Recomendações e Arquitetura-Alvo](#12-recomendações-de-arquitetura-e-estado-alvo)
+
+**Legenda de estereótipos (C4):** `«person»` ator humano · `«system»` sistema externo · `«container»` unidade executável/deployável (processo, app, banco) · `«component»` agrupamento lógico dentro de um contêiner. Setas = fluxo/dependência rotulada com protocolo.
+
+---
+
+## 1. C4 Nível 1 — Contexto de Sistema
+
+**Objetivo:** o sistema Criptotrade como uma caixa preta, seus usuários e sistemas externos.
+
+```mermaid
+graph TB
+ Op(["👤 Operador / Trader
«person»
aprova ordens (HITL), ajusta risco,
observa KPIs"])
+
+ subgraph SYSBND["Criptotrade — Plataforma de Trading com IA + HITL «system»"]
+ SYS["Backend Python + Console Web
gera sinais, valida risco, executa
ordens (paper), audita tudo"]
+ end
+
+ EX(["🏦 Exchange / Market Data
«system» (CCXT)
OHLCV, ordens (paper por padrão)"])
+ LLM(["🧠 LLM Provider
«system» (Gemini/OpenAI/Anthropic)
Chain-of-Thought / reflexão (opcional)"])
+ PROM(["📈 Prometheus + Grafana
«system»
métricas técnicas e de negócio"])
+ SENTRY(["🐞 Sentry
«system» (opcional)
captura de erros 5xx"])
+
+ Op -->|"HTTPS: console React + dashboard Streamlit"| SYS
+ SYS -->|"CCXT (async); dry-run offline por padrão"| EX
+ SYS -.->|"HTTPS (opcional, degradação graciosa)"| LLM
+ PROM -->|"scrape /metrics (HTTP, 15s)"| SYS
+ SYS -.->|"eventos 5xx (opcional)"| SENTRY
+```
+
+**Notas de arquitetura:**
+- **HITL é primeira-classe:** o operador não é observador passivo — é parte do fluxo de execução (aprova/rejeita ordens acima do limiar de autonomia).
+- **Fail-safe por padrão:** o único caminho para a exchange roda em `EXCHANGE_DRY_RUN=true` (mercado sintético offline); trading real exige override explícito no ambiente de deploy.
+- **Dependências externas são todas opcionais/degradáveis:** LLM, Sentry e até a exchange (dry-run) podem faltar sem derrubar o núcleo.
+
+---
+
+## 2. C4 Nível 2 — Contêineres
+
+**Objetivo:** as unidades executáveis/deployáveis e como se comunicam. Este é o diagrama de arquitetura central.
+
+```mermaid
+graph TB
+ Op(["👤 Operador «person»"])
+
+ subgraph Boundary["Criptotrade «system»"]
+ NGINX["nginx «container»
reverse proxy + TLS
serve console estático · proxy /v1"]
+ CONSOLE["Console React «container»
docs/design/pages (dist estático)
SPA hash-routing"]
+ API["API «container»
FastAPI/uvicorn :8000
REST /v1 + SSE + /metrics"]
+ LOOP["Loop Orquestrador «container»
python -m src.orchestration.main_loop
ciclo de trading (sem porta)"]
+ DASH["Dashboard «container»
Streamlit :8501
console operacional alternativo"]
+ SQLITE[("SQLite (WAL) «container»
orders · cycle_events · open_positions
circuit_breaker · backtest_jobs · journal · ledger_events")]
+ JSONL[("Arquivos append-only «container»
ledger JSONL · alerts JSONL · XES · memória")]
+ PROM["Prometheus «container» :9090"]
+ GRAF["Grafana «container» :3000"]
+ PG[("Postgres «container» (perfil scale)")]
+ REDIS[("Redis «container» (perfil scale)")]
+ end
+
+ EX(["Exchange (CCXT) «system»"])
+ LLM(["LLM «system»"])
+
+ Op -->|HTTPS| NGINX
+ NGINX -->|serve estático| CONSOLE
+ CONSOLE -->|"REST /v1 + SSE (envelope APIResponse, X-API-Key)"| NGINX
+ NGINX -->|"HTTP proxy /v1 /health"| API
+ Op -.->|HTTP alternativo| DASH
+ DASH -->|"httpx REST /v1"| API
+
+ API -->|read/write| SQLITE
+ API -->|read/write| JSONL
+ LOOP -->|read/write| SQLITE
+ LOOP -->|append| JSONL
+ LOOP -->|analyze_and_trade| EX
+ API -->|market data| EX
+ LOOP -.->|CoT/reflexão| LLM
+ API -.->|CoT| LLM
+
+ PROM -->|scrape /metrics| API
+ GRAF -->|query| PROM
+ API -. "scale: DATABASE_URL" .-> PG
+ API -. "scale: REDIS_URL (rate limit)" .-> REDIS
+
+ classDef store fill:#e8eef7,stroke:#4a6fa5;
+ class SQLITE,JSONL,PG,REDIS store;
+```
+
+**Decisões arquiteturais-chave (o "porquê"):**
+
+| Decisão | Racional | ADR |
+|---|---|---|
+| **API e Loop como contêineres separados** | Lifecycle independente: restart da API não para o trading, e vice-versa | ADR-002 |
+| **Estado compartilhado via SQLite WAL** (sem RPC/fila) | Cross-process simples e durável; dois processos no mesmo host/volume | ADR-001/003 |
+| **Paper trading first (`EXCHANGE_DRY_RUN`)** | Segurança: zero conexão real por padrão | ADR-001 |
+| **Uma imagem Python para API/Loop/Dashboard** | Diferem só pelo `command`; simplifica build/deploy | — |
+| **Console React estático (esbuild + nginx)** | Sem servidor Node em produção; SPA servida como arquivos | — |
+| **Postgres/Redis opt-in (perfil `scale`)** | Escala horizontal sem mudar código (abstração de DB + rate limiter) | ADR-005 |
+
+---
+
+## 3. C4 Nível 3 — Componentes do Contêiner API
+
+**Objetivo:** organização interna da FastAPI (`src/api/*`).
+
+```mermaid
+graph TB
+ subgraph API["Contêiner API (FastAPI) «container»"]
+ direction TB
+ MW["Cadeia de Middleware «component»
RequestId → Prometheus → RateLimit →
SecurityHeaders → CORS → APIKey"]
+ subgraph Routers["Routers /v1 «component»"]
+ R1[metrics · trades · risk]
+ R2[orders · hitl]
+ R3[agents · config · process]
+ R4[market · backtest · journal · alerts]
+ end
+ DEPS["deps.py «component»
providers @lru_cache:
ledger · order_store · alert_store/bus ·
registry · exchange · metrics_calc · hitl_store"]
+ SCHEMAS["schemas.py «component»
DTOs Pydantic + envelope APIResponse[T]"]
+ OBS["observability «component»
PrometheusMiddleware · DomainMetricsCollector"]
+ end
+
+ subgraph Domain["Serviços de domínio/infra (fora da API)"]
+ OS["OrderStore (hitl)"]
+ LG["TradingLedger (core)"]
+ MC["PortfolioMetricsCalculator"]
+ AR["AgentRegistry"]
+ AX["ExchangeClient + analysis/*"]
+ BT["BacktestEngine/MC/WF"]
+ AB["AlertStore + AlertBus"]
+ end
+ DB[("SQLite/Postgres")]
+
+ MW --> Routers
+ Routers --> DEPS
+ Routers --> SCHEMAS
+ DEPS --> OS
+ DEPS --> LG
+ DEPS --> MC
+ DEPS --> AR
+ DEPS --> AX
+ R2 --> OS
+ R4 --> BT
+ R4 --> AB
+ R4 --> AX
+ R1 --> MC
+ OS --> DB
+ LG --> DB
+ OBS --> LG
+```
+
+**Notas:**
+- **Middleware em cadeia (fail-closed em prod):** `_enforce_prod_security()` recusa boot em produção sem `API_KEYS` + `CORS_ORIGINS` explícito. Rate limit por IP real (nginx repassa `X-Forwarded-*`).
+- **Injeção via `deps.py`:** providers `@lru_cache(maxsize=1)` (singletons por processo), exceto `get_metrics_calculator` (fresco a cada request para refletir o ledger). `reset_singletons()` para testes.
+- **Contrato tipado:** `schemas.py` → OpenAPI → `openapi.d.ts` (gate de drift no CI).
+
+---
+
+## 4. C4 Nível 3 — Componentes do Contêiner Loop Orquestrador
+
+**Objetivo:** organização interna do processo de trading (`src/orchestration/*` + agentes).
+
+```mermaid
+graph TB
+ subgraph LOOP["Contêiner Loop «container»"]
+ MAIN["main_loop «component»
init_db · from_env · sinais SIGTERM"]
+ OL["OrchestratorLoop «component»
run_forever/run_cycle · intervalo 60s ·
heartbeat · registry.record_cycle"]
+ SQ["SquadOrchestrator «component»
pipeline analyze_and_trade"]
+ CB["CircuitBreaker «component»
perda diária 4% / 3 losses → pausa 24h"]
+ subgraph Agents["Agentes de trading «component»"]
+ SA[StrategyAgent
CoT + TA + regime]
+ RA[RiskAgent
reflexão + guardrails]
+ EA[ExecutionAgent
ReAct paper fill]
+ end
+ PS["PositionStore «component»
book de posições paper"]
+ HB["approval_handler «component»
OrderStore.wait_for_decision"]
+ end
+ DB[("SQLite compartilhado")]
+ EX(["Exchange (CCXT)"])
+ LG["TradingLedger + XES"]
+
+ MAIN --> OL
+ OL --> SQ
+ SQ --> CB
+ SQ --> SA
+ SQ --> RA
+ SQ --> EA
+ SQ --> PS
+ SQ --> HB
+ SA --> EX
+ EA --> EX
+ HB --> DB
+ PS --> DB
+ CB --> DB
+ OL --> LG
+ SQ --> LG
+```
+
+**Notas:**
+- **Pipeline invariante:** `Strategy → (fecha SL/TP) → Risk → Guardrails → HITL → Execution → fill/ledger`, com circuit breaker no início. Fail-soft: falha de um símbolo emite `agent_cycle_failed` e o loop continua.
+- **Recuperação de restart:** `reload_open_positions()` restaura book + breaker do SQLite (evita posições zumbi / streak esquecida).
+- Detalhe de classes e sequência: ver `docs/uml/arquitetura-uml.md` §5.2 e §6.1.
+
+---
+
+## 5. Arquitetura de Dados
+
+**Objetivo:** o modelo de persistência. O sistema usa **dois estilos**: (a) tabelas SQLite/Postgres para estado cross-process, (b) arquivos append-only (JSONL/JSON) para auditoria e memória.
+
+### 5.1 ERD — estado relacional (SQLite WAL / Postgres)
+
+```mermaid
+erDiagram
+ orders {
+ TEXT id PK "ord_xxxx"
+ TEXT pair
+ TEXT side "buy|sell (CHECK)"
+ REAL quantity "> 0"
+ REAL price "> 0"
+ TEXT strategy
+ TEXT agent_id
+ REAL confidence
+ INTEGER critical "0/1"
+ REAL position_size_pct
+ REAL stop_loss
+ REAL take_profit
+ TEXT status "pending|approved|filled|rejected|cancelled"
+ INTEGER auto_approved "0/1"
+ TEXT created_at
+ TEXT resolved_at
+ TEXT filled_at
+ }
+ open_positions {
+ TEXT order_id PK
+ TEXT symbol
+ TEXT side
+ REAL entry_price
+ REAL quantity
+ REAL stop_loss
+ REAL take_profit
+ TEXT opened_at
+ }
+ circuit_breaker_state {
+ INTEGER id PK "singleton (id=1)"
+ REAL tripped_at
+ INTEGER consecutive_losses
+ REAL daily_loss_pct
+ }
+ cycle_events {
+ INTEGER id PK "autoincrement"
+ TEXT agent_id
+ TEXT cycled_at "idx(agent_id,cycled_at)"
+ }
+ backtest_jobs {
+ TEXT id PK
+ TEXT status "running|done|error"
+ TEXT config_json
+ TEXT result_json
+ TEXT error
+ TEXT created_at
+ TEXT completed_at
+ }
+ journal_entries {
+ INTEGER id PK "autoincrement"
+ TEXT setup
+ INTEGER emotion_before "1..10"
+ INTEGER emotion_after "1..10"
+ INTEGER stop_defined "0/1"
+ INTEGER plan_followed "0/1"
+ REAL pnl_pct
+ TEXT created_at
+ }
+ ledger_events {
+ INTEGER id PK "autoincrement"
+ TEXT timestamp
+ TEXT event_type "idx"
+ TEXT data "JSON"
+ }
+
+ orders ||..o| open_positions : "id ≈ order_id (lógico, sem FK)"
+ orders ||..o{ ledger_events : "gera eventos (lógico)"
+ cycle_events }o..|| orders : "agent_id (lógico)"
+```
+
+> **Nota de design importante:** as tabelas **não têm foreign keys** entre si. É deliberado — são datasets de coordenação cross-process (bridge HITL, contadores, book) desenhados para escrita concorrente por dois processos sem contenção de lock. As relações acima são **lógicas** (tracejadas). O `ledger_events` é o *event store* de onde métricas são derivadas.
+
+### 5.2 Data stores completos (relacional + arquivos)
+
+```mermaid
+graph LR
+ subgraph Relacional["SQLite WAL (default) / Postgres (scale)"]
+ T1[orders]
+ T2[open_positions]
+ T3[circuit_breaker_state]
+ T4[cycle_events]
+ T5[backtest_jobs]
+ T6[journal_entries]
+ T7[ledger_events]
+ end
+ subgraph Arquivos["Append-only (volume ./data)"]
+ F1[["ledger JSONL
auditoria"]]
+ F2[["event log XES
process mining"]]
+ F3[["alerts JSONL"]]
+ F4[["agent_memories.jsonl
+ Chroma opcional"]]
+ F5[["trade_journal.json"]]
+ F6[["loop_heartbeat.json"]]
+ end
+ Writers["API · Loop · Agentes"] --> Relacional
+ Writers --> Arquivos
+ Metrics["PortfolioMetricsCalculator"] -->|deriva| T7
+ ProcessAPI["/v1/process/events"] -->|lê| F2
+```
+
+**Notas:**
+- **Event Sourcing leve:** métricas de portfólio (Sharpe, win rate, drawdown, P&L) **não são armazenadas** — são recalculadas relendo `ledger_events` (`position_closed`, `order_fill`). Fonte única de verdade.
+- **Migração de portabilidade:** `src/core/db.py` abstrai `connection()`; migrations em `migrations/` (+ `migrations/postgres/`) rodadas por `init_db()` (idempotente, ambos processos).
+- **Dívida conhecida (ADR-003):** o XES/alerts ainda em JSONL; a migração completa para SQLite está deferida.
+
+---
+
+## 6. Fluxo de Dados (DFD)
+
+**Objetivo:** como um sinal vira ordem, fill e métrica — o caminho do dado ponta a ponta.
+
+```mermaid
+flowchart LR
+ EX([Exchange OHLCV]) -->|fetch_ohlcv| SA[StrategyAgent
TA + regime + CoT]
+ SA -->|signal + confidence| RA[RiskAgent + Guardrails]
+ RA -->|approved signal| OSUB[OrderStore.submit]
+ OSUB -->|"notional ≤ threshold?"| AUTO{auto?}
+ AUTO -->|sim| FILL[fill local]
+ AUTO -->|não| PEND[(orders: pending)]
+ PEND -->|"PATCH /v1/orders (operador)"| APPR[(orders: approved)]
+ APPR -->|loop wait_for_decision| EXEC[ExecutionAgent paper fill]
+ FILL --> LED[(ledger_events)]
+ EXEC --> LED
+ LED -->|position_closed / order_fill| MC[PortfolioMetricsCalculator]
+ MC -->|APIResponse| UI[Console / Dashboard]
+ RA -.->|rejeição| AL[(alerts)]
+ AL -.->|SSE /v1/alerts| UI
+ LED -.->|XES| PM["/v1/process/events → process mining"]
+```
+
+**Notas:** dois "portões" no fluxo — **guardrails** (risco por ordem) e **HITL** (aprovação por valor). Todo evento econômico passa pelo ledger, do qual métricas e process mining derivam.
+
+---
+
+## 7. Vista de Runtime / Processos
+
+**Objetivo:** o que roda concorrentemente e como sincronizam.
+
+```mermaid
+graph TB
+ subgraph P1["Processo: API (uvicorn) — N workers possíveis"]
+ A1[event loop asyncio]
+ A2[middleware + routers]
+ A3[backtest asyncio.create_task]
+ end
+ subgraph P2["Processo: Loop — SINGLE INSTANCE"]
+ B1[run_forever: ciclo a cada 60s]
+ B2[wait_for_decision: poll 2s]
+ end
+ subgraph P3["Processo: Dashboard (Streamlit)"]
+ C1[httpx → API]
+ end
+ SHARED[("SQLite WAL
(única fonte de sincronização)")]
+
+ A2 -->|resolve/decide| SHARED
+ B2 -->|poll status| SHARED
+ B1 -->|record_cycle / positions| SHARED
+ A3 -->|persist jobs| SHARED
+ C1 -->|HTTP| A2
+
+ note1["Sincronização = estado no SQLite.
Sem fila/mensageria.
Latência de aprovação ≤ poll_interval (2s).
Timeout 300s → cancel (fail-closed)."]
+ P2 -.-> note1
+```
+
+**Ponto de atenção arquitetural:** o **loop é single-instance por design** (ADR-005). O modelo HITL por *polling* de SQLite não é trivialmente replicável para N loops (precisaria de leader-election ou locks de linha). A API, sim, escala horizontalmente (estado externo + rate limiter Redis).
+
+---
+
+## 8. Implantação
+
+**Objetivo:** as três topologias versionadas. Nós = contêineres; setas rotuladas com protocolo/porta.
+
+### 8.1 Dev — `docker-compose.yml`
+
+```mermaid
+graph TB
+ Host["🖥️ Host dev (todas as portas publicadas)"]
+ app["app :8000
EXCHANGE_DRY_RUN=true"]
+ orch["orchestrator (sem porta)
INTERVAL=60s"]
+ dash["dashboard :8501"]
+ prom["prometheus :9090"]
+ graf["grafana :3000"]
+ pg["postgres :5432
(perfil scale)"]
+ rd["redis :6379
(perfil scale)"]
+ vol[("volume ./data + ./logs
compartilhado app↔orchestrator")]
+
+ Host --- app & orch & dash & prom & graf & pg & rd
+ app --- vol
+ orch --- vol
+ dash -->|API_URL=http://app:8000| app
+ prom -->|scrape| app
+ graf -->|query| prom
+ app -. scale .- pg
+ app -. scale .- rd
+ orch -->|healthcheck| hc["scripts/healthcheck_loop.py
(lê loop_heartbeat.json)"]
+```
+
+### 8.2 Prod self-contained — `docker-compose.prod.yml`
+
+```mermaid
+graph TB
+ Net["rede edge 172.28.0.0/24 (nginx = .2)"]
+ subgraph Public["Exposto (host)"]
+ nginx["nginx :80/:443
console baked + proxy /v1
reload a cada 6h"]
+ certbot["certbot sidecar
renova certs 2x/dia (HTTP-01)"]
+ end
+ subgraph Internal["Interno (sem porta de host)"]
+ appp["app
--proxy-headers
--forwarded-allow-ips 172.28.0.2
APP_ENV=production (fail-closed)"]
+ orchp["orchestrator"]
+ promp["prometheus"]
+ end
+ volp[("volume ./data")]
+ Browser(["Browser"])
+
+ Browser -->|HTTPS 443| nginx
+ nginx -->|HTTP proxy| appp
+ certbot -.-> nginx
+ appp --- volp
+ orchp --- volp
+ promp -->|scrape| appp
+ Net --- nginx & appp & orchp & promp
+```
+
+### 8.3 VPS atrás de gateway compartilhado — `docker-compose.vps.yml`
+
+```mermaid
+graph LR
+ GW["btv-nginx-prod (global-ingress)
rede externa btv-prod-net"]
+ subgraph CT["rede interna criptotrade_internal (expose only)"]
+ va["criptotrade-app:8000"]
+ vf["criptotrade-frontend:80
(console.Dockerfile)"]
+ vd["criptotrade-dashboard:8501"]
+ vo["criptotrade-orchestrator"]
+ vp["criptotrade-prometheus"]
+ end
+ GW -->|"/v1/ ·/health → app:8000"| va
+ GW -->|"/ → frontend:80"| vf
+ vf -->|"same-origin /v1 (API_BASE='')"| GW
+ vd --> va
+ vp --> va
+```
+
+**Notas:**
+- **Superfície pública mínima em prod:** só nginx/gateway publica 80/443; app/loop/prometheus ficam internos.
+- **Confiança de proxy explícita:** app só aceita `X-Forwarded-*` de `172.28.0.2` → rate-limit por IP real.
+- **Uma imagem Python** reusada; console em imagem nginx separada (`deploy/console.Dockerfile`, multi-stage node→nginx).
+
+---
+
+## 9. Pipeline CI/CD
+
+**Objetivo:** os gates automáticos (GitHub Actions) que protegem `master`.
+
+```mermaid
+flowchart TB
+ TRIG["push master / claude/** · PR → master"]
+
+ subgraph WF1["workflow: Python CI"]
+ SS["secret-scan
gitleaks"]
+ TEST["test
ruff (gate correção) + ruff full (info)
pytest --cov (fail-under 72%)"]
+ DB2["docker-build (needs: test)
build imagem Python · compose config -q (prod+vps)
build console image · nginx -t (certs efêmeros)"]
+ CB["console-build
npm ci · npm run build (esbuild)
gen:types + git diff --exit-code (drift gate)
upload console-dist"]
+ E2E["console-e2e
build · playwright chromium (mock data)"]
+ end
+ subgraph WF2["workflow: Phase 1-3 Validation (rápido)"]
+ PV["validate-phases
lean deps · ruff módulos novos
smoke import src.api.main:app
pytest (metrics/alerts/db/api/orders)"]
+ end
+
+ TRIG --> SS & TEST & CB & E2E & PV
+ TEST --> DB2
+
+ style DB2 fill:#eef7ee,stroke:#4a7
+ style CB fill:#eef7ee,stroke:#4a7
+```
+
+**Gates notáveis:**
+- **Drift do contrato** (`gen:types` + `git diff --exit-code openapi.d.ts`): backend Python e front JS **não podem divergir**.
+- **Compose/nginx validados no CI** com certs auto-assinados descartáveis.
+- **Cobertura com piso** (`--cov-fail-under=72`, `pyproject.toml`), com meta de subir para 80%.
+- **Dois workflows**: um completo (pesado) e um rápido por fases (deps enxutas).
+
+---
+
+## 10. Arquitetura de Observabilidade
+
+**Objetivo:** os quatro pilares — métricas, logs, traces, e o diferencial de **process mining**.
+
+```mermaid
+graph TB
+ subgraph App["API + Loop"]
+ PM["PrometheusMiddleware
http_requests_total · duration histogram
(label = template de rota, cardinalidade limitada)"]
+ DM["DomainMetricsCollector
criptotrade_* gauges do ledger
(open_positions, pnl, win_rate, sharpe)"]
+ LOG["Logs JSON estruturados
python-json-logger + request_id
(RequestIdMiddleware + ContextVar)"]
+ TR["AgentObserver / SpanRecord
trajectory export (reasoning log)"]
+ XES["TradingLedger.log_process_event
event log XES (case_id, activity, actor)"]
+ HB["loop_heartbeat.json
liveness do loop (sem HTTP)"]
+ SEN["Sentry (opcional)
captura 5xx"]
+ end
+
+ PROM["Prometheus :9090
scrape /metrics (15s)"]
+ GRAF["Grafana :3000
dashboards provisionados
(performance/availability/business/ai)"]
+ PMINE["/v1/process/events
→ análise de processo"]
+ SSE["/v1/alerts (SSE)
AlertBus fan-out"]
+
+ PM --> PROM
+ DM --> PROM
+ PROM --> GRAF
+ XES --> PMINE
+ App --> SSE
+ HB --> HCK["scripts/healthcheck_loop.py"]
+ SEN -.-> SentryCloud([Sentry])
+```
+
+**Diferencial:** o **event log XES** (`log_process_event` emite `agent_cycle_started/completed/failed`, `order_submitted/approved/rejected/filled/cancelled`) habilita *process mining* real do fluxo de trading — raro em sistemas deste porte. Correlação por `request_id` liga logs a requests HTTP.
+
+---
+
+## 11. Arquitetura de Segurança
+
+**Objetivo:** as camadas de defesa (rede, autenticação, risco, execução, segredos).
+
+```mermaid
+graph TB
+ subgraph Perimetro["Perímetro"]
+ N["nginx/gateway
TLS 1.2/1.3 · HSTS · único público"]
+ end
+ subgraph Rede["Isolamento de rede"]
+ NET["app/loop/prom internos
(edge 172.28.0.0/24 ou criptotrade_internal)
--forwarded-allow-ips (confia só no proxy)"]
+ end
+ subgraph AppSec["Segurança da aplicação (middleware)"]
+ AUTH["APIKeyMiddleware (X-API-Key)
dev: fail-open · prod: fail-closed
(_enforce_prod_security no boot)"]
+ RL["RateLimitMiddleware
30/min write · 200/min read (por IP)"]
+ SH["SecurityHeadersMiddleware
CSP · X-Frame-Options DENY · nosniff · HSTS"]
+ CORS["CORS: '*' dev / allowlist explícito prod"]
+ end
+ subgraph Trading["Segurança de trading"]
+ DRY["EXCHANGE_DRY_RUN obrigatório
(ExchangeClient recusa iniciar sem)"]
+ GR["GuardrailSystem
position size · stop loss · risk-reward"]
+ HITL["HITL fail-closed
(sem handler → nega)"]
+ CBK["CircuitBreaker (perda diária/streak)"]
+ end
+ subgraph Exec["Segurança de execução (tools)"]
+ SC["SecurityConfig
FORBIDDEN_PATTERNS · validate_tool_call"]
+ SB["SecureToolSandbox
DockerSandbox (network off, timeouts)"]
+ end
+ subgraph Segredos["Gestão de segredos"]
+ GL["gitleaks (CI)"]
+ ENV[".env / .env.prod (host, fora do git)"]
+ end
+
+ N --> NET --> AppSec --> Trading
+ AppSec -.-> Exec
+ Segredos -.-> AppSec
+```
+
+**Notas:**
+- **Fail-closed onde importa:** produção recusa boot com auth aberto; HITL nega sem handler; exchange recusa sem `EXCHANGE_DRY_RUN`.
+- **Defesa em profundidade:** perímetro (TLS) → rede (isolamento) → app (auth/rate/headers) → domínio (guardrails/HITL/breaker) → execução (sandbox).
+- **Pontos a consolidar** (ver §12): duas políticas de validação de ordem (`GuardrailSystem` vs `SecurityConfig.validate_order`) e dois modelos de autonomia.
+
+---
+
+## 12. Recomendações de Arquitetura e Estado-Alvo
+
+### 12.1 Achados priorizados
+
+| # | Severidade | Achado | Recomendação |
+|---|---|---|---|
+| R1 | 🔴 Alta | **Cluster "BuildToValue" paralelo** (`UnifiedOrchestrator` + planning/routing/consensus/chains/parallel + agentes de engenharia) **não exercitado pelo trading** | Extrair para pacote opcional `src/experimental/` ou remover; medir cobertura real. Reduz superfície e confusão. |
+| R2 | 🔴 Alta | **Colisões de nome** (`SquadOrchestrator`×2, `AdaptivePlanner`×2, `ContinuousEvaluator`×2, `MemoryStore`×2, `Guardrail`×2) | Renomear por propósito (ex.: `TradingSquad` vs `A2ASquad`). Evita imports errados. |
+| R2b | 🟠 Média | **Duas fundações de agente** (`BaseAgent` async vs `SafeAgentBase` sync) sem ponte | Escolher uma base única ou documentar explicitamente os dois papéis. |
+| R3 | 🟠 Média | **Duas políticas de validação de ordem** e **dois modelos de autonomia** (US$ threshold vs trust-score) | Eleger fonte única de política de risco/autonomia. |
+| R4 | 🟠 Média | **HITL por polling de SQLite** acopla loop↔API ao arquivo; loop single-instance | Migrar coordenação para **Postgres `LISTEN/NOTIFY`** ou Redis pub/sub → menor latência + caminho para multi-loop. |
+| R5 | 🟡 Baixa | **Kelly/proteções prontos mas não plugados** no sizing do pipeline | Ligar `PositionSizer`/`KellyCriterion`/`CapitalProtections` no `SquadOrchestrator._position_quantity`. |
+| R6 | 🟡 Baixa | **Namespace `/v1/agents/...` servido por dois routers** (`agents` e `config`) | Consolidar num router único. |
+| R7 | 🟡 Baixa | **XES/alerts ainda em JSONL** (ADR-003 deferido) | Concluir migração para SQLite/Postgres quando o volume justificar. |
+| R8 | 🟡 Baixa | **Frontend "classic scripts" com globals `window.*`** | Migrar para ES modules + bundler quando o console crescer. |
+
+### 12.2 Métricas qualitativas de acoplamento
+- **Núcleo estável correto:** `core` é dependência de quase tudo e depende de pouco (config/db) — *Stable Dependencies Principle* respeitado.
+- **God Object no cluster genérico:** `UnifiedOrchestrator` compõe 9 subsistemas — acoplamento eferente alto, reforça R1.
+- **Bom uso de inversão de dependência:** callbacks (`alert_sink`, `state_db_provider`, `fill_callback`, `approval_handler`) quebram ciclos de import consistentemente.
+
+### 12.3 Arquitetura-alvo proposta (evolução incremental)
+
+```mermaid
+graph TB
+ Op(["👤 Operador"])
+ subgraph Target["Estado-alvo"]
+ NGINX["nginx/gateway (TLS)"]
+ FE["Console (ES modules + bundler)"]
+ API["API FastAPI (N réplicas)"]
+ subgraph Loops["Loop(s) de trading"]
+ L1["Loop primário (leader)"]
+ L2["Loop standby (leader-election)"]
+ end
+ BUS{{"Coordenação:
Postgres LISTEN/NOTIFY
ou Redis pub/sub"}}
+ PGDB[("Postgres
estado + event store XES unificado")]
+ RISK["Serviço de Risco único
(GuardrailSystem + Kelly + proteções)"]
+ OBS["Observabilidade
Prometheus + Grafana + traces"]
+ end
+ EX(["Exchange"])
+ LLM(["LLM (opcional)"])
+
+ Op -->|HTTPS| NGINX --> FE
+ FE -->|REST+SSE| API
+ API --> BUS
+ L1 --> BUS
+ L2 -. standby .-> BUS
+ API --> PGDB
+ L1 --> PGDB
+ L1 --> RISK
+ API --> RISK
+ L1 --> EX
+ L1 -.-> LLM
+ API --> OBS
+ L1 --> OBS
+
+ style BUS fill:#fff3cd,stroke:#c90
+ style RISK fill:#d4edda,stroke:#4a7
+ style Loops fill:#e8eef7,stroke:#4a6fa5
+```
+
+**Roteiro incremental (sem big-bang):**
+1. **Higiene** (R1, R2): isolar/remover o cluster genérico e renomear colisões — baixo risco, alto ganho de clareza.
+2. **Consolidação de política** (R3, R5, R6): uma fonte de risco/autonomia; plugar Kelly; unificar rotas de agentes.
+3. **Coordenação** (R4, R7): trocar polling SQLite por `LISTEN/NOTIFY` no Postgres (já suportado pela abstração de DB) e unificar o event store — habilita multi-loop com leader-election.
+4. **Frontend** (R8): migrar para ES modules/bundler.
+
+Cada passo é independente e preserva o invariante central: **paper-trading-first, HITL fail-closed, tudo auditável no ledger/XES**.
+
+---
+
+> **Diagramas em Mermaid** (renderizam nativamente no GitHub). Nomes de contêineres/componentes/tabelas preservados conforme o código-fonte e migrations. Para detalhe de **classes** e sequências, ver `docs/uml/arquitetura-uml.md`.
diff --git "a/docs/documenta\303\247\303\243o/mapeamento-dados.md" "b/docs/documenta\303\247\303\243o/mapeamento-dados.md"
new file mode 100644
index 0000000..7802073
--- /dev/null
+++ "b/docs/documenta\303\247\303\243o/mapeamento-dados.md"
@@ -0,0 +1,446 @@
+# Mapeamento Completo de Dados — Criptotrade
+
+> **Dicionário de dados exaustivo de todo o repositório.** Mapeia **todos os dados que circulam no sistema** — entradas, saídas, processamento intermediário e estado — em cada módulo e em cada linguagem (Python, JavaScript/JSX, TypeScript, HTML, SQL, YAML, `.env`, Docker). Inclui deliberadamente os dados **"perdidos na própria classe"** (atribuídos e nunca lidos).
+>
+> Produzido por análise estática dos scripts do repositório, com referências `arquivo:linha`. Complementa `docs/uml/arquitetura-uml.md` (classes) e `docs/architecture/arquitetura.md` (C4).
+
+## Nota sobre linguagens
+Varredura recursiva do repositório: **Python** 168 arquivos (núcleo), **React/JSX + JS** ~24 (console em `docs/design/pages/`), **TypeScript** 1 (`openapi.d.ts`, contrato gerado), **HTML** 2, **SQL** 4 (migrations), **YAML** vários (config/monitoring). **Rust: 0 arquivos** — não existe código Rust neste repositório; onde a metodologia pediria mapeamento Rust, registra-se a ausência.
+
+## Legenda de classificação de dado
+Cada dado é marcado por seu papel no fluxo:
+
+| Marca | Papel |
+|---|---|
+| **E** | Entrada (input externo: parâmetro, env, HTTP, arquivo, exchange, LLM) |
+| **S** | Saída (retorno de função, resposta HTTP, escrita em store, alerta) |
+| **I** | Intermediário (variável local, atributo de estado, dict transformado) |
+| **P** | Persistido (SQLite/Postgres, JSONL/JSON, arquivo) |
+| **X** | Dado morto / "perdido na classe" (atribuído e nunca lido depois) |
+
+---
+
+## Índice
+- [1. Dados de entrada — configuração e ambiente](#1-dados-de-entrada--configuração-e-ambiente)
+- [2. Dados persistidos — bancos e arquivos](#2-dados-persistidos--bancos-e-arquivos)
+- [3. Estruturas de dados canônicas em trânsito](#3-estruturas-de-dados-canônicas-em-trânsito)
+- [4. Catálogo por módulo — backend Python](#4-catálogo-por-módulo--backend-python)
+- [5. Dados da camada API (DTOs, rotas, middleware)](#5-dados-da-camada-api)
+- [6. Dados do frontend (React/JS) e contrato TS](#6-dados-do-frontend-reactjs-e-contrato-ts)
+- [7. Ciclo de vida ponta-a-ponta de um dado](#7-ciclo-de-vida-ponta-a-ponta-de-um-dado)
+- [8. Dados "perdidos na classe" / declarados-mas-não-usados](#8-dados-perdidos-na-classe--declarados-mas-não-usados)
+- [9. Anomalias e inconsistências de dados](#9-anomalias-e-inconsistências-de-dados)
+
+---
+
+## 1. Dados de entrada — configuração e ambiente
+
+### 1.1 Variáveis de ambiente (todas as lidas no código)
+
+| Var | Default | Tipo/coerção | Consumido em |
+|---|---|---|---|
+| `APP_ENV` | `"development"` | str, compara `"production"` | `config.py:16`, `api/main.py:67,195`, `routes/config.py:62` |
+| `LOG_LEVEL` | `"INFO"` | str | `config.py:17` |
+| `LOG_FORMAT` | `"text"` | text\|json | `config.py:18` |
+| `GOOGLE_API_KEY` / `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` | `None` | str | `config.py:21-22`, `llm_client.py:51-54` |
+| `LLM_ENABLED` | `false` | truthy (`1/true/yes/on`) | `llm_client.py:61` |
+| `LLM_PROVIDER` | `"google"` | str | `llm_client.py:47` |
+| `LLM_MODEL` | (default por provider) | str | `llm_client.py:76` |
+| `EXCHANGE` | `"binance"` | str | `config.py:25`, `deps.py:83` |
+| `EXCHANGE_API_KEY` / `EXCHANGE_API_SECRET` | `None` | str | `config.py:26-27`, `deps.py:85-86` |
+| `EXCHANGE_TESTNET` | `true` | bool | `config.py:28`, `deps.py:84` |
+| `EXCHANGE_DRY_RUN` | **obrigatório (sem default)** | bool; unset → `RuntimeError` | `exchange_client.py:41-48`, `routes/config.py:58` |
+| `ORDER_ROUTING` | `"paper"` | paper\|live | `exchange_client.py:62-64` |
+| `DRY_RUN_BASE_PRICE` | `"50000"` | float | `exchange_client.py:50` |
+| `DRY_RUN_BASE_PRICES` | `""` | CSV `PAIR=PRICE` | `exchange_client.py:53` |
+| `MARKET_PAIRS` | `BTC/USDT,ETH/USDT,SOL/USDT,BNB/USDT,XRP/USDT` | CSV | `pairs.py:21` |
+| `SYMBOLS` | (unset → `BTC/USDT`) | CSV (subset de MARKET_PAIRS) | `orchestrator_loop.py:50` |
+| `INITIAL_CAPITAL` | `10000` | float | `config.py:31`, `deps.py:24`, `hitl/orders.py:392` |
+| `MAX_POSITION_SIZE_PCT` | `5.0` | float | `config.py:32`, `risk_agent.py:20`, `guardrails.py:13` |
+| `STOP_LOSS_PCT` | `3.0` | float | `config.py:33`, `risk_agent.py:21` |
+| `MAX_DAILY_LOSS_PCT` | `5.0` | float | `config.py:34`, `risk_agent.py:22` (**X** no RiskAgent) |
+| `MAX_CONCURRENT_POSITIONS` | `3` | int | `config.py:35` |
+| `AUTONOMY_LEVEL` | `1` (config) / `2` (hitl) | int (0-3 no hitl) | `config.py:38`, `hitl/config.py:64` |
+| `HITL_APPROVAL_REQUIRED` | `true` | bool | `config.py:39` |
+| `ORCHESTRATOR_INTERVAL_SECONDS` | `60` | int [10,3600] | `orchestrator_loop.py:74`, `routes/config.py:60` |
+| `DATABASE_URL` | `sqlite:///./data/trading.db` | str; `postgres*`→PG | `config.py:42`, `db.py:35` |
+| `CHROMA_PERSIST_DIR` | `./data/chroma` | str | `config.py:48` |
+| `PROMETHEUS_PORT` | `9090` | int | `config.py:54` |
+| `LEDGER_DIR` | `.buildtovalue/ledger` | path | `config.py:89`, `ledger.py:30`, `alerts.py:57` |
+| `JOURNAL_PATH` | `data/trade_journal.json` | path | `journal/trade_journal.py:106` |
+| `API_KEYS` | `""` (fail-open) | CSV allowlist | `api/main.py:53` |
+| `CORS_ORIGINS` | `"*"` | CSV | `api/main.py:72,218` |
+| `SENTRY_DSN` / `SENTRY_TRACES_SAMPLE_RATE` | `""` / `0.0` | str/float | `api/main.py:185,196` |
+| `REDIS_URL` | (unset → in-memory) | str | `ratelimit.py:88` |
+| `API_URL` / `API_KEY` / `REFRESH_INTERVAL` (dashboard) | `http://localhost:8000` / `""` / `5` | str/str/int | `dashboard/app.py:16-18` |
+| Infra: `POSTGRES_DB/USER/PASSWORD`, `GF_SECURITY_ADMIN_USER/PASSWORD` | (compose) | str | `docker-compose.yml` |
+
+> Constantes de caminho (`config.py:85-90`): `PROJECT_ROOT` (=`src/`), `DATA_DIR`, `LOGS_DIR`, `LEDGER_DIR`, `CONFIG_DIR`. Settings inner `Config`: `env_file=".env"`, `extra="ignore"`.
+
+### 1.2 Configuração YAML (dados de política)
+
+**`config/strategies/risk_params.yaml`** — lido por `routes/risk.py:28`. Seções e chaves-dado:
+- `position_limits`: `max_position_size_pct 5.0`, `min_position_size_pct 1.0`, `max_concurrent_positions 3`, `max_exposure_per_asset 15.0`
+- `stop_loss`: `mandatory true`, `default_pct 3.0`, `max_allowed_pct 5.0`, `trailing_enabled false`
+- `take_profit`: `mandatory false`, `default_pct 9.0`, `min_risk_reward_ratio 1.5`
+- `loss_limits`: `max_loss_per_trade_pct 3.0`, `max_daily_loss_pct 5.0`, `max_weekly_loss_pct 10.0`, `max_monthly_loss_pct 15.0`; `circuit_breaker{enabled, trigger_daily_loss_pct 4.0, trigger_consecutive_losses 3, cooldown_period_hours 24}`
+- `diversification`, `leverage{allowed false, max 1.0}`, `order_types{allowed[limit], prohibited[market,stop_market], slippage{max 0.5, simulated_in_paper 0.2}}`
+- `market_conditions{volatility.max 80, liquidity.min_24h_volume_usd 1e8, trend.min_indicators_aligned 2}`
+- `capital{initial 10000, reserve_pct 20, max_deployed_pct 80}`, `performance_targets{sharpe 1.5, max_drawdown 15, win_rate 55, profit_factor 1.8}`
+- `signal_validation{min_confidence 0.6, filters[volume_surge,spread_check,momentum_confirmation]}`, `fees{exchange_fee_pct 0.1, slippage 0.2, break_even 0.6}`
+- `strategies{dca_optimized(size 2.0,entries 3,spacing 1.0), grid_trading(disabled), momentum(disabled)}`, `mode{current paper_trading, transition_criteria...}`
+
+**`config/agents/constitution.yaml`** (v6.1): `agents.{strategy(autonomy 2, chain_of_thought),risk(autonomy 3, reflection),execution(autonomy 1, react)}` cada com role/goals/capabilities/constraints/tools/performance_targets; `collaboration.flow: strategy→risk→hitl→execution→ledger`; `global_guardrails.{security,risk_management,operational}`; `audit.ledger_path .buildtovalue/ledger/trades.jsonl`.
+
+**`config/prometheus.yml`**: `scrape_interval 15s`, job `app` → `app:8000`.
+**`metrics.yaml` / `metrics-advanced.yaml`**: catálogos `{metric,target,alert,dashboard}` (technical/business/ai_squad/agent). **`config/prompts/advanced_agent_prompts.py`**: `AGENT_PROMPTS: dict[str,str]` com 4 prompts (`architect_with_cot`, `developer_with_react`, `auditor_with_reflection`, `consensus_facilitator`).
+
+### 1.3 Dados de entrada externos (runtime)
+- **Exchange (CCXT)** — OHLCV `[ts_ms,o,h,l,c,v]`, ticker, order book, balance, ordens (ver §4.8 `exchange_client`). Em dry-run vêm de `synthetic_market` (determinístico).
+- **LLM** — entrada `(system, user)` strings; saída `str` ou JSON `{confidence, thesis}` (Strategy) / `{note, hidden_risk}` (Risk).
+- **HTTP** — corpo/query/headers das rotas `/v1/*` (ver §5).
+
+---
+
+## 2. Dados persistidos — bancos e arquivos
+
+### 2.1 Tabelas SQLite/Postgres (migrations)
+
+| Tabela | Colunas (tipo · constraint) | Índices | Origem |
+|---|---|---|---|
+| **orders** (`001`) | `id TEXT PK`, `pair TEXT NN`, `side TEXT NN CHECK(buy\|sell)`, `quantity REAL NN >0`, `price REAL NN >0`, `strategy TEXT`, `agent_id TEXT`, `confidence REAL`, `reason TEXT`, `critical INT NN=0`, `position_size_pct REAL`, `stop_loss REAL`, `take_profit REAL`, `status TEXT NN='pending' CHECK(pending\|approved\|rejected\|cancelled\|filled)`, `operator_note TEXT`, `operator_id TEXT`, `auto_approved INT NN=0`, `created_at TEXT NN`, `resolved_at TEXT`, `filled_at TEXT` | `status`, `created_at` | HITL bridge |
+| **cycle_events** (`001`) | `id INT PK AUTOINC`, `agent_id TEXT NN`, `cycled_at TEXT NN` | `(agent_id,cycled_at)` | contador de ciclos |
+| **journal_entries** (`002`) | `id INT PK AUTOINC`, `setup TEXT NN`, `emotion_before INT NN CHECK 1..10`, `emotion_after INT CHECK 1..10`, `stop_defined INT NN=0`, `plan_followed INT NN=0`, `pnl_pct REAL`, `note TEXT`, `created_at TEXT NN=now` | `created_at`, `emotion_before` | diário |
+| **backtest_jobs** (`003`) | `id TEXT PK`, `status TEXT NN CHECK(running\|done\|error)`, `config_json TEXT`, `result_json TEXT`, `error TEXT`, `created_at TEXT NN`, `completed_at TEXT` | `status`, `created_at` | jobs async |
+| **open_positions** (`004`) | `order_id TEXT PK`, `symbol TEXT NN`, `side TEXT NN`, `entry_price REAL NN`, `quantity REAL NN`, `stop_loss REAL`, `take_profit REAL`, `opened_at TEXT NN` | `symbol` | book paper |
+| **circuit_breaker_state** (`004`) | `id INT PK CHECK(id=1)` (singleton), `tripped_at REAL`, `consecutive_losses INT NN=0`, `daily_loss_pct REAL NN=0.0` | — | breaker |
+| **ledger_events** (criada em `ledger.py:48`) | `id {autoincrement_pk}`, `timestamp TEXT NN`, `event_type TEXT NN`, `data TEXT NN` (JSON) | `event_type` | event store |
+| **schema_migrations** (`db.py:205`) | `version TEXT PK`, `applied_at TEXT NN` | — | migrations |
+
+> **Sem foreign keys** entre tabelas (design de coordenação cross-process). `db.py` abstrai `connection()`; PG traduz `?`→`%s` (`db.py:102`); `_Row`/`_PgConn` adaptam PG à API sqlite3.
+
+### 2.2 Event store — payloads do `ledger_events.data` (JSON)
+
+Cada `log_*` de `TradingLedger` grava `(timestamp, event_type, data)`:
+
+| Método | `event_type` | chaves de `data` | linha |
+|---|---|---|---|
+| `log_signal(agent,signal)` | `signal_generated` | `agent`, `signal`(dict) | `ledger.py:74` |
+| `log_validation(agent,validation)` | `risk_validation` | `agent`, `validation`(dict) | `:78` |
+| `log_execution(agent,execution)` | `order_executed` | `agent`, `execution`(dict) | `:82` |
+| `log_hitl_approval(approved,order,user)` | `hitl_approval` | `approved`(bool), `order`(dict), `user`(str="default") | `:86` |
+| `log_process_event(case_id,activity,actor,attributes)` | `process_event` | `case_id`, `activity`, `actor`, `attributes`(dict) | `:90` |
+| `log_fill(...)` | `order_fill` | `order_id`, `symbol`, `side`(lower), `price`, `quantity`, `notional`(=p·q), `fee`, `strategy`, `agent` | `:116` |
+| `log_position_closed(...)` | `position_closed` | `order_id`, `symbol`, `side`(lower), `entry_price`, `exit_price`, `quantity`, `fee`, `gross_pnl`, `pnl`(=gross−fee), `pnl_pct`, `opened_at` | `:149` |
+| `log_decision(event_type,data)` | arbitrário | ex.: CircuitBreaker → `circuit_breaker_tripped`/`_reset` com `{reason}` | `:60` |
+
+**Atividades XES** (via `log_process_event`): `agent_cycle_started/completed/failed` (loop), `order_submitted/approved/rejected/cancelled/filled` (OrderStore), `hitl_level_changed` (HITLConfigStore).
+
+### 2.3 Arquivos append-only (JSONL/JSON)
+
+| Arquivo | Registro (chaves) | Escrito por |
+|---|---|---|
+| `trades.jsonl` (legado) | — (histórico) | `ledger.py` |
+| `alerts.jsonl` | `severity, type, message, agent_id, pair, auto_action, id, occurred_at` | `AlertStore.append` (`alerts.py`) |
+| `agent_memories.jsonl` | `agent, decision`(dict), `timestamp` | `AgentMemorySystem.remember_decision` (`agent_memory.py:39`) |
+| `trade_journal.json` | `{order_id: asdict(TradeEntry)}` (objeto único) | `TradeJournal._save` (`trade_journal.py:211`) |
+| `ab_tests.jsonl` | `{timestamp, winner, scores, statistical_significance}` — **`str()` repr, não JSON** | `ab_testing.py:60` |
+| `loop_heartbeat.json` | `{ts, cycle_id}` | `heartbeat.write_heartbeat` |
+
+---
+
+## 3. Estruturas de dados canônicas em trânsito
+
+Estes são os **dicts que fluem entre módulos** — o dado "vivo" do sistema. Chaves exatas e origem.
+
+### 3.1 `task` (entrada de cada `agent.execute`)
+- **Strategy** `{symbol, timeframe}` — `squad_orchestrator.py:207`
+- **Risk** `{signal, portfolio:{available_capital, capital_base}}` — `:241`
+- **Execution** `{signal, human_approved, quantity}` — `:272`
+
+### 3.2 `analysis` (StrategyAgent._analyze_market → strategy_agent.py:160)
+`symbol, timeframe, current_price, trend`(bullish\|bearish\|None)`, regime, eligible_strategies:list[str], indicators:TechnicalIndicators, support_resistance, fibonacci_levels:dict, volume_profile, rsi_divergence, macd_divergence, market_extreme, stub_used:bool, _ohlcv:list` (I; `_ohlcv` removido antes de logar). Versão stub (`:446`) espelha as chaves com valores fixos (`regime="sideways"`, `eligible_strategies=["grid","dca"]`).
+
+### 3.3 `signal` (StrategyAgent._generate_signal → strategy_agent.py:218) — **o dado central do sistema**
+`action`(BUY\|SELL\|HOLD)`, entry_price, stop_loss, take_profit, position_size_pct`(default 2.0)`, strategy, regime, market_context:{atr,bb_middle,volume_ratio}|None`. `symbol` é adicionado depois pelo orquestrador via `setdefault` (`:214`); `reason` só existe nos caminhos HOLD.
+
+### 3.4 `market_data` — **DOIS formatos distintos**
+- **Nested** (Grid/MeanReversion) — `_build_market_data` (`strategy_agent.py:383`): `symbol, current_price, trend, regime, rsi, macd_histogram, at_bollinger_lower, ma_20, ma_50, volume_24h, avg_volume, indicators:TechnicalIndicators, support_resistance, volume_profile, _raw_ohlcv`
+- **Flat** (DCA e backtest) — `engine.py:234`: `current_price, rsi(=50), macd_histogram(=0), at_bollinger_lower(=False), ma_20, ma_50, volume_24h, avg_volume(=1), _raw_ohlcv` (sem `indicators`)
+
+### 3.5 Resultados dos agentes
+- **strategy_result** (`strategy_agent.py:89`): `success, agent, signal, confidence, analysis, llm_used, llm_thesis, stub_used`
+- **risk_result** (`risk_agent.py:64`): `success, agent, approved, validation:{approved,issues,warnings,confidence,[requires_review],refined,reflection_applied}, confidence`
+- **execution_result** (`execution_agent.py:50`): `success, agent, order_id, executed_price, fee, confidence`
+- **analyze_and_trade** (`squad_orchestrator.py:297`): `success, order_id, signal, confidence` — ou `{success:False, reason, ...}` nos caminhos de rejeição
+- **run_cycle** (`orchestrator_loop.py:149`): `{cycle_id, ran:list[str], failures:list[{agent_id,error}]}`
+
+### 3.6 `Order` (dataclass HITL — hitl/orders.py:48)
+`pair, side, quantity, price, strategy, agent_id, confidence, reason, critical=False, position_size_pct=0.0, stop_loss=None, take_profit=None, status=pending, operator_note=None, operator_id=None, auto_approved=False, id="ord_"+hex8, created_at, resolved_at=None, filled_at=None`; property `notional=price*quantity`; `guardrail_view()→{position_size_pct,action=side.upper(),entry_price=price,stop_loss,take_profit}`.
+
+### 3.7 `_open_positions[order_id]` (book paper — squad_orchestrator.py:374)
+`{symbol, side(lower), entry_price, quantity, stop_loss|None, take_profit|None, opened_at(iso)}` — espelhado em SQLite via `PositionStore`.
+
+---
+
+## 4. Catálogo por módulo — backend Python
+
+> Formato: por arquivo/classe, os dados com marca **E/S/I/P/X**. Atributos de estado listados no `__init__`.
+
+### 4.1 `src/agents/`
+
+**`base_agent.py` — BaseAgent** (`:17`)
+- **I** `self.agent_type`(:21), `self.agent_id`(uuid,:22), `self.created_at`(:23), `self.confidence_threshold=0.6`(:24), `self.memory`(:29), `self.tools=[]`(:35)
+- `log_decision` → **I/S** `entry={timestamp,agent,agent_id,decision}`(:48) → grava em memory + retorna
+
+**`strategy_agent.py` — StrategyAgent** (detalhado em §3.2–3.5)
+- **I** `_sr_detector`, `_div_detector`, `_strategy_cache:dict`(key→instância), `_llm`
+- `_llm_assess` **I** `context`{symbol,regime,trend,action,strategy,entry_price,stop_loss,take_profit,deterministic_confidence,rsi,macd_hist,bb_percent,volume_ratio}(:344) → LLM → `(blended,thesis)`
+- `_stub_analysis` **I** `stub_ind` TechnicalIndicators hardcoded (current_price=50000, rsi=45, bb_percent=0.25, atr=50, volume_ratio=1.2...)(:434)
+- confiança: blend `0.6·strategy + 0.4·agent` (:57), com LLM `0.5/0.5` (:373), clamp [0.10,0.95]
+
+**`risk_agent.py` — RiskAgent**
+- **I** `guardrails=GuardrailSystem()`(:18), `max_position_size_pct`(:20), `stop_loss_pct`(:21), **X** `max_daily_loss_pct`(:22 — nunca lido), `_llm`
+- `_validate_signal` **E** signal.{position_size_pct,entry_price,stop_loss}, portfolio.{available_capital,capital_base} → **S** `{approved,issues,warnings,confidence}`(:119)
+- `_reflect_on_validation` **S** `{missed_anything,too_strict,suggestions,[llm_note]}`
+- `_refine_validation` **S** `final` = validation + `{[requires_review],refined,reflection_applied}`
+
+**`execution_agent.py` — ExecutionAgent**
+- **I** `exchange`, `paper_trading=True`
+- `_simulate_order` **E** signal.{symbol,action}, quantity → exchange.create_order → **S** `{success,order_id,executed_price,fee,status,message}`(:112)
+
+**`registry.py`**
+- **I** `AGENT_REGISTRY` (5 AgentInfo: strategy/risk/execution implemented, recovery/exploration stub) (:38)
+- **I** `AGENT_PARAMS` catálogo estático por agente (confidence_threshold, tools, reasoning_pattern, autonomy_level, e p/ risk: max_position_size_pct/stop_loss_pct/max_daily_loss_pct/min_risk_reward_ratio) (:52)
+- **I** `self._cycles:dict`, `self._last_action:dict`, `self._cycles_date` — **P** grava `cycle_events`
+- `status()` **S** `{id,domain,implemented,description,status,cycles,last_action_at,params}`
+
+**`behavioral_guard.py`** — constantes REVENGE_LOSS_STREAK=2, REVENGE_SIZE_MULTIPLIER=1.50, EUPHORIA_WIN_STREAK=3, EUPHORIA_SIZE_MULTIPLIER=1.20, OVERCONFIDENCE_MARGIN=0.15. `BehavioralAlert{detected,kind,message,action,recommended_size_multiplier,recommended_confidence_cap}`. `check` **E** new_trade.{position_size_pct,confidence}, trade_history[].{position_size_pct,pnl}, win_rate.
+
+**Agentes de engenharia** (não no pipeline de trading; executados por `UnifiedOrchestrator`):
+- `architect_agent` **S** `{reasoning{problem_analysis,constraints,applicable_patterns,trade_offs,recommendation,confidence=0.85,reasoning_steps},plan{goal,architecture,components,patterns,risks,mitigations},adr:str}`
+- `developer_agent` **I** `history[{iteration,thought,action,observation}]`, max_iterations=5 → **S** `{status,history,final_output,confidence}`
+- `auditor_agent` **S** audit `{issues,warnings,passed,confidence}`; validate_results `{approved,confidence,issues,agent_scores}`
+- `designer_agent` **S** `{pattern,theme,components,colors{primary,secondary,background},typography,responsive,accessibility,confidence}`
+- `ops_agent` **S** deploy `{strategy,stages,rollback_plan,health_checks,scaling{min_instances=2,max_instances=10,target_cpu=70}}`, monitoring `{metrics,alerts,dashboards,logging}`
+- `supervisor_agent` `SupervisorAgent{orchestrator:Agent, specialists:list[Agent]}` → `run(task:str)→str`
+- `recovery_agent`/`exploration_agent` dataclasses `{remediation_tool}`/`{scanner_tool}` → `arun→str`
+
+### 4.2 `src/orchestration/`
+
+**`squad_orchestrator.py`**
+- **CircuitBreaker** — constantes DAILY_LOSS_LIMIT_PCT=4.0, CONSECUTIVE_LOSS_LIMIT=3, COOLDOWN_SECONDS=86400. **I/P** `_tripped_at`, `_consecutive_losses`, `_daily_loss_pct` (persistidos em `circuit_breaker_state`). `record_trade_result(pnl_pct)` acumula; `_trip/_reset` gravam ledger `circuit_breaker_tripped/_reset{reason}`.
+- **SquadOrchestrator** — **I** agentes, ledger, circuit_breaker, `_last_order_ref`, `_open_positions`, `_positions` (§3.7). Pipeline `analyze_and_trade` (§3.5, §7). `_available_capital = initial + _realized_pnl − open_notional`. `_position_quantity = (capital·size_pct/100)/price`. Emite `Alert` type=`data_fallback`/`risk_rejection` (severity high).
+
+**`orchestrator_loop.py`** — constantes MIN/MAX/DEFAULT_INTERVAL=10/3600/60. `run_cycle` **I** `cycle_id`, `ran_agents`, `failures`; grava XES `agent_cycle_started/completed/failed`. `from_env` monta ExchangeClient/ledger/registry/OrderStore/handler/SquadOrchestrator. `AgentExecutionError.agent_id`.
+
+**`position_store.py`** — `PositionStore.load_all()→{order_id:{symbol,side,entry_price,quantity,stop_loss,take_profit,opened_at}}`; `save/load_circuit_state→{tripped_at,consecutive_losses,daily_loss_pct}`.
+
+**`heartbeat.py`** — `{ts,cycle_id}` (HEARTBEAT_FILENAME="loop_heartbeat.json").
+
+**`unified_orchestrator.py`** (squad de engenharia, fora do trading) — **I** planner/router/consensus/parallel/evaluator/autonomy/memory + **X** `sandbox`, `chain_manager` (criados, nunca usados). `execute_complex_task` **S** `{success,task_id,plan,consensus,results,validation,confidence}`.
+
+### 4.3 `src/strategies/`
+- `STRATEGY_REGISTRY = {"dca":DCA, "grid":Grid, "mean_reversion":MeanReversion}` (`__init__.py:17`)
+- **MeanReversion** params `{rsi_oversold=30, rsi_overbought=70, risk_reward=2.0, atr_mult=2.0}`. **E** `market_data.{indicators(rsi,bb_lower/upper/middle,atr,volume_ratio,stochastic_k),current_price,regime}`. **S** `{action,direction,entry,stop_loss,take_profit,position_size_pct=2.0,confidence,reason}` ou HOLD `{action,confidence=0.05,reason}`.
+- **Grid** params `{grid_levels=10,grid_spacing_pct,total_size_pct,size_per_level}`. **E** `indicators.{ema_fast,ema_slow,atr,bb_middle,volume_ratio}, volume_profile.poc, regime`. **S** `{action,direction,entry,stop_loss,take_profit=None,position_size_pct,total_position_size_pct,grid_levels:{buy:[],sell:[]},confidence,reason}`.
+- **DCA** params `{position_size_pct=2.0,num_entries=3,spacing_pct=1.0,stop_loss_pct=3.0,rsi_oversold=35,min_volume_ratio=0.8}`. **E** flat `market_data.{symbol,current_price,ma_20,ma_50,rsi,macd_histogram,at_bollinger_lower,volume_24h,avg_volume}`. **S** `{action:"DCA_ENTRY"|"WAIT",signal:{symbol,strategy,action,entries:[{entry_number,price,size_pct}],avg_entry_price,stop_loss,take_profit,total_position_size_pct,risk_reward_ratio=3.0,timestamp},confidence,reasoning}`.
+
+### 4.4 `src/analysis/` (dataclasses = DTOs de análise)
+- **TechnicalIndicators** (20 campos `float|None`): sma_20/50/200, ema_fast, ema_slow, rsi, stochastic_k/d, macd_line/signal/hist, bb_upper/middle/lower, bb_percent, atr, volume_ratio, obv, current_price (`indicators.py:18`). OHLCV in = `[ts_ms,o,h,l,c,v]`. `MIN_CANDLES=50`.
+- **DivergenceResult** `{detected,kind,description}`; **TrendAlignment** `{primary,secondary,minor,aligned,direction}`; MultiTimeframeTrend TIMEFRAMES `{primary:1w,secondary:1d,minor:1h}`.
+- **SRLevel** `{price,kind,strength,last_touch}`; **SRLevels** `{support,resistance,zones:[SRLevel]}`; `fibonacci_levels→{"0.0%".."100.0%":price}`.
+- **VolumeProfileResult** `{poc,value_area_high,value_area_low,low_volume_nodes:[]}`.
+- **PatternResult** `{pattern,confidence,direction,target_price,candle_index,description}`.
+- **regime_detector** (funções): `detect_regime→"unknown"|"chaotic"|"strong_uptrend"|"strong_downtrend"|"sideways"`; `_REGIME_STRATEGY_MAP{strong_uptrend:[dca],strong_downtrend:[],sideways:[grid,dca],chaotic:[],unknown:[dca]}`; `detect_market_extreme→"EUFORIA…"|"PÂNICO…"|None`.
+
+### 4.5 `src/backtest/`
+- **BacktestTrade** `{candle_index,action,entry_price,exit_price,position_size_pct,pnl_usdt,pnl_pct,stop_loss,take_profit,exit_reason}`.
+- **BacktestResult** `{total_trades,win_rate,total_pnl_usdt,total_pnl_pct,max_drawdown_pct,sharpe_ratio,profit_factor,avg_win_pct,avg_loss_pct,trades:[BacktestTrade]}` + prop `expectancy`. Constantes commission 0.001, slippage 5bps, warmup 50.
+- **WindowResult**/`WalkForwardResult` (validação walk-forward; MAX_SHARPE_DEVIATION=0.30). **MonteCarloResult** `{n_simulations,median/p5/p95_pnl_pct,max_simulated_drawdown,pct_profitable,rejected}`.
+- `_build_market_data` emite o formato **flat** (§3.4) → só DCA o consome plenamente.
+
+### 4.6 `src/risk/`
+- **KellyCriterion** `{win_rate,avg_win_pct,avg_loss_pct,capital=10000,n_trades}` → `full_kelly()|None` (min 30 trades), `fractional_kelly(0.25)` clamp [0.5,5.0], `ruin_risk()`. Constantes KELLY_FRACTION=0.25, MIN/MAX_POSITION_PCT=0.5/5.0, MIN_SAMPLE_FOR_KELLY=30.
+- **PositionSizer** `compute(entry,stop)→size%` clamp [0.5,5.0].
+- **CapitalProtections** `check(daily,weekly,monthly)→ProtectionResult{status:DrawdownStatus,message,size_multiplier(1.0/0.5/0.0),can_trade}`. Limites DAILY=3.0/WEEKLY=6.0/MONTHLY=15.0/WARN=2.4. **DrawdownStatus** = OK/WARN/DAILY_PAUSE/WEEKLY_REDUCED/MONTHLY_SUSPEND.
+
+### 4.7 `src/safety/`
+- **GuardrailSystem** `validate_order(order)→(bool,list[str])`. **E** order.{position_size_pct,stop_loss,entry_price,action,take_profit,market_context:{atr,bb_middle,volume_ratio}}. Regras: position≤max, stop obrigatório e do lado certo, risk_reward≥2.5, market atr/bb_middle≤0.10 e volume_ratio≥0.3. `alert_sink:Callable[[str],None]`.
+- **SecurityConfig** (dataclass) `{MAX_POSITION_SIZE_PCT=5.0,MAX_STOP_LOSS_PCT=3.0,MAX_DAILY_LOSS_PCT=5.0,MAX_CONCURRENT_POSITIONS=3,MAX_EXECUTION_TIME_SECONDS=30,FORBIDDEN_PATTERNS[leverage.*10x,margin.*call,liquidation,all.*in,100%.*position]}`; ClassVars FORBIDDEN_TOOL_NAMES(rm,delete_resource,format_disk,drop_database), SENSITIVE_PARAM_PATTERNS(rm -rf,drop table,delete from,format), ALLOWED_EXCHANGES{binance,coinbase,kraken}, HIGH_RISK_ACTIONS. `validate_order` **E** order.{position_size_pct,notes,exchange}; `validate_tool_call(tool_name,params)`.
+
+### 4.8 `src/core/` (resumo — detalhe completo em §1, §2)
+- **config.Settings** (23 campos env, §1.1); funções `get_risk_params/get_resource_limits/get_autonomy_config`.
+- **db** `connection()`, `upsert`, `init_db`, `_Row`, `_PgConn` (§2.1).
+- **ledger.TradingLedger** — event store (§2.2); read `_row_to_entry→{timestamp,event_type,data}`.
+- **metrics.PortfolioMetrics** (14 campos §5) derivado de `position_closed`+`order_fill`. `_PERIOD_DAYS{1d,7d,30d,90d,all}`.
+- **exchange_client.ExchangeClient** — `fetch_ticker→{symbol,last,close,bid,ask,timestamp,info}`, `fetch_ohlcv→[[ts,o,h,l,c,v]]`, `fetch_order_book→{bids,asks}`, `fetch_balance→{USDT:{free,used,total},BTC:{...}}`, `create_order/_create_paper_order→{id="PAPER_"+hex8,symbol,type,side,amount,price,average,filled,remaining,status,fee:{cost,currency},timestamp,datetime,info}`. `simulated_orders:dict`. Slippage 0.002, fee 0.001.
+- **alerts** — Alert `{severity,type,message,agent_id,pair,auto_action,id,occurred_at}`; AlertStore JSONL; AlertBus fila `asyncio.Queue(maxsize=100)`; `make_guardrail_sink`. SEVERITIES=(low,medium,high,critical).
+- **llm_client.LLMClient** `reason(system,user)→str?`, `reason_json→dict?`; `_DEFAULT_MODELS{google:gemini-1.5-flash,openai:gpt-4o-mini,anthropic:claude-haiku-4-5-...}`.
+- **ratelimit** InMemory `_buckets:dict[str,list[float]]` / Redis `INCR+pexpire` (key `ns:key`); janela 60s.
+- **pairs** DEFAULT_PAIRS; `parse_pairs`/`allowed_pairs`/`is_allowed`.
+- **synthetic_market** `_DEFAULT_BASES{ETH:3000,SOL:150,BNB:600,XRP:0.5}`; `synthetic_ticker/ohlcv/order_book`.
+- **request_context** `request_id_var:ContextVar(default="-")`; RequestIdLogFilter.
+- **safe_agent_base** — Plan/PlanCreation/AgentExecution dataclasses; GuardrailSuite + 4 guardrails; MemoryManager.
+
+### 4.9 `src/hitl/`
+- **OrderStore** (§3.6, §5.6): `submit/resolve/mark_filled/cancel/wait_for_decision`. Auto-approve quando `notional≤threshold & !critical & risk_ok`. `make_approval_handler` mapeia signal→Order (§7).
+- **config.HITLConfigStore** `snapshot()→{current_level,threshold_usdt,level_description,min_level,max_level,pending_orders_count,human_approved_today,human_rejected_today,last_changed_at,last_changed_by,levels:[{level,threshold_usdt,description}]}`. AUTONOMY_LEVELS {0:0.0,1:500,2:1000,3:5000}, DEFAULT=2.
+- **progressive_autonomy.ProgressiveAutonomyManager** (não ligado às rotas): `autonomy_levels{0..3}`, `agent_trust_scores:dict`, `action_history:[{timestamp,agent,action,success,trust_score,autonomy_level}]`; score +0.02/−0.1; result `{approved,feedback,modifications}`.
+
+### 4.10 `src/memory/`, `src/journal/`, `src/tools/`, `src/evaluation/`, misc
+- **AgentMemorySystem** — **X** `short_term:dict` (nunca escrito); episodic JSONL `{agent,decision,timestamp}`; Chroma opcional. `recall/recall_similar`.
+- **IntelligentForgetting** — `MemoryStore.data:{key:{relevance}}`; remove relevance<0.1.
+- **TradeJournal** — TradeEntry (identidade+setup+psicologia+resultado, §4 detalhe); JournalStats (9 campos); JSON `{order_id:asdict}`.
+- **MCPToolRegistry** — `tools:dict[str,callable]`; core tools `analyze_code_quality→{file,complexity,coverage,issues}`, `generate_tests→str`, `optimize_performance→{suggestions,estimated_gain}`.
+- **rag_tool** VectorDBClient `_collection_name="btf-default"`; RAGTool.retrieve.
+- **sandbox** DockerSandbox `{image=python:3.11-slim,network_disabled=True}`; SecureToolSandbox `{docker_sandbox,execution_timeout=30,` **X** `memory_limit_mb=512,` **X** `cpu_quota=0.5,allow_unsandboxed=False}`; result `{tool,output|params,sandboxed,[message]}`.
+- **evaluation** — `continuous_eval.ContinuousEvaluator` (**X** `baseline`) `evaluate_trajectory→{task_completion,efficiency,safety}` (lê trajectory.{completed,resource_usage,guardrail_violations}); `continuous_evaluator.ContinuousEvaluator` (dataclass) metrics `{task_success_rate,average_confidence,` **X** `user_satisfaction,error_rate,response_time_p95}`; `ab_testing` result `{winner,scores{A,B},statistical_significance}`.
+- **consensus.WeightedConsensusEngine** `agent_weights{architect,developer,auditor,designer,ops:{domínio:peso}}`; `reach_consensus→{decision,consensus_strength,decision_maker,dissenting_opinions:[{agent,score}]}`.
+- **routing.LearningRouter** `route_performance{route_hash:{attempts,successes,total_latency,success_rate,avg_latency}}`; `smart_route→request["preferred_route"]|"default"`.
+- **planning** — `hierarchical_planner` plan `{goal,steps,alternatives,confidence}`; `adaptive_planner` plan `{plan_id,task_id,goal,steps:[{step,action,description,estimated_time,dependencies,can_fail}],estimated_duration,created_at,adaptive,confidence}`; `adaptive_replanner` (dup) `{plan,result,attempts}`.
+- **chains.ChainStep** `{name,execute}` / ResilientPromptChain `{steps,max_retries=3}`. **parallel.ParallelResourceManager** `limits{max_concurrent:5}`.
+- **protocols.MCPServer** card `{name,skills,endpoint}`, handle_request(type discover|execute). **protocols.SquadOrchestrator** `delegate_task→{plan,implementation}`.
+- **utils.observability** SpanRecord `{operation,metadata,start_time,reasoning_log,end_time}`; `export_trajectory→{trace_id,spans:[...]}`. **utils.MemoryStore** key/value. **tool_utils.wrap_sync_tool**.
+- **main.py** `MemoryStore.set("usuario","João")`; **orchestrator.py** AgentOrchestrator (base_agent+evaluator+rag_tool+observer+mcp_server) `execute_with_monitoring→(AgentExecution,evaluation)`.
+
+---
+
+## 5. Dados da camada API
+
+### 5.1 Middleware (dados lidos/escritos, em ordem de execução)
+| Middleware | Dado | `arquivo:linha` |
+|---|---|---|
+| RequestId | lê/escreve header `X-Request-ID`; grava `request_id_var` | `request_id.py:23-29` |
+| Prometheus | Counter `http_requests_total{method,path,status}`, Histogram `http_request_duration_seconds{method,path}`; gauges de domínio `criptotrade_{open_positions,total_trades,portfolio_value_usdt,realized_pnl_usdt,win_rate,sharpe_ratio}` | `observability.py:18-89` |
+| RateLimit | `request.client.host` → key `ip:w|r`; WRITE=30/READ=200/min; 429 `{error,message,retry_after,docs}` | `main.py:128-164` |
+| SecurityHeaders | escreve CSP, X-Frame-Options=DENY, X-Content-Type-Options=nosniff, Referrer-Policy, HSTS max-age=300, X-XSS-Protection=0 | `main.py:109-125` |
+| CORS | `CORS_ORIGINS`; métodos GET,POST,PATCH; headers X-API-Key,Content-Type | `main.py:216` |
+| APIKey | lê header `X-API-Key`; PUBLIC_PATHS isentos; 401 `{error,message,docs}` | `main.py:84-106` |
+
+**Handlers de exceção**: 422 `{error:validation_error,message,field,docs}`; 404 `{error:not_found,message,docs}`; 500 `{error:internal_error,message,docs}` (`main.py:265-313`).
+
+### 5.2 Envelope e DTOs (schemas.py) — contrato de saída/entrada
+- **APIResponse[T]** `{data:T, meta:Meta?, links:Links?(alias _links)}`; **Meta** `{total,page=1,per_page=20,timestamp}`; **Links** `{self,related?}`; **ErrorResponse** `{error,message,field?,docs="/v1/docs"}`.
+- **PortfolioMetricsOut** (14): sharpe_ratio?, win_rate?, max_drawdown, profit_factor?, total_trades, open_positions, portfolio_value_usdt, pnl_period_usdt, pnl_period_pct, exposure_pct, initial_capital_usdt, period, calculated_at, has_data.
+- **OrderCreate** (E): pair(regex `^[A-Z]{2,10}/[A-Z]{2,10}$`), side(OrderSide), quantity(>0), price(>0), strategy(min1), agent_id(min1), confidence(0-1), reason(min10), critical=False, position_size_pct(0-100], stop_loss(>0, **obrigatório**), take_profit?(>0).
+- **OrderDecisionPatch** (E): decision(`^(approve|reject)$`), operator_note?(**obrigatório em reject**), operator="operator".
+- **OrderOut** (S): id,pair,side,quantity,price,notional,status,strategy,agent_id,confidence,reason,critical,auto_approved,operator_note?,operator_id?,position_size_pct?,stop_loss?,take_profit?,rr?,created_at,resolved_at?,filled_at?.
+- **HITLConfigOut**, **AutonomyLevelOut/Patch**, **AgentStatusOut/ConfigOut**, **ProcessEventOut**{case_id,activity,actor,timestamp,attributes}, **AlertOut**, **ClosedTradeOut**, **EquityPoint**{t,equity,drawdown}.
+- Mercado: **CandleOut**{t,o,h,lo,c,v} (nota: `lo`, não `l`), **TickerOut**, **MacdOut/StochOut/BollingerOut**, **IndicatorsOut** (rsi,macd,stoch,bb,atr,atr_pct,ema9/21,sma20/50/200,obv_trend,volume_ratio,current_price,as_of), **RegimeOut**, **SRLevelOut/LevelsOut**, **VolumeProfileBin/Out**, **PatternOut**, **ConfidenceFactor**, **SignalOut**{action,entry,stop,take_profit,position_size_pct,rr,strategy,confidence,reason,confidence_factors,valid_until,as_of}, **TFSnapshot**, **ConfluenceOut**.
+- Risco: **ProtectionOut**{scope,value,limit,status,action}, **CircuitBreakerOut**{status,triggers,cooldown_hours,cooldown_remaining}, **KellyOut** (9 campos), **RiskConfigOut** (14 campos), **RiskConfigPatch** (7 opcionais + confirm).
+- Backtest: **BacktestConfigIn**{strategy=dca,pair=BTC/USDT,initial_capital=10000,commission_pct=0.1,slippage_bps=5,monte_carlo_sims=1000}, **BacktestResultOut**, **MonteCarloOut**, **WalkForwardFold/Out**, **BacktestJobOut**{job_id,status,result?,error?}.
+- Journal: **JournalEntryCreate**{setup(min3),emotion_before(1-10),emotion_after?(1-10),stop_defined,plan_followed,pnl_pct?,note?}, **JournalEntryOut** (+id,created_at), **EmotionBand**, **JournalMetricsOut**.
+- Config: **ConfigOut**{exchange,dry_run,initial_capital,orchestrator_interval_seconds,autonomy_level,app_env}, **ConfigPatch**, **AlertsConfigPatch**{revenge_size_multiplier,euphoria_size_multiplier,overconfidence_margin,risk_of_ruin_alert_pct}.
+
+### 5.3 Rotas `/v1/*` — entradas (query/path/body) → saída
+| Rota | Entrada | Saída |
+|---|---|---|
+| `GET /v1/metrics` | period(1d\|7d\|30d\|90d\|all), symbol? | APIResponse[PortfolioMetricsOut] |
+| `GET /v1/metrics/equity` | period, symbol? | List[EquityPoint] |
+| `GET/PATCH /v1/hitl/config` | (PATCH) AutonomyLevelPatch; level 3 requer confirm | HITLConfigOut |
+| `GET /v1/orders` | status?,pair?,limit(1-500),offset | List[OrderOut] + Meta |
+| `POST /v1/orders` | OrderCreate | OrderOut (201 filled/202 pending/422 rejected) |
+| `PATCH /v1/orders/{id}/status` | OrderDecisionPatch | OrderOut (404/409) |
+| `GET /v1/agents[/{id}][/config]` | path id | AgentStatusOut/ConfigOut (404/501) |
+| `GET /v1/process/events` | case_id?,limit(1-1000) | List[ProcessEventOut] |
+| `GET /v1/alerts` (SSE) | severity?,replay(0-200) | EventSource {alert,heartbeat} |
+| `GET /v1/alerts/history` | severity?,since?,limit,page | List[AlertOut] |
+| `GET /v1/market/pairs` | — | List[str] |
+| `GET /v1/market/{pair}/{candles,ticker,indicators,regime,levels,volume-profile,patterns,signal,confluence}` | tf,limit,bins? | DTO respectivo (503 se dados indisponíveis) |
+| `GET /v1/risk/{protections,circuit-breaker,kelly,config}` · `PATCH /v1/risk/config` | RiskConfigPatch (confirm) | DTOs de risco |
+| `POST /v1/backtest/{run,montecarlo,walkforward}` · `GET /v1/backtest/jobs/{id}` | BacktestConfigIn | BacktestJobOut/MonteCarloOut/WalkForwardOut |
+| `GET/POST /v1/journal` · `GET /v1/journal/metrics` | JournalEntryCreate | JournalEntryOut / JournalMetricsOut |
+| `GET /v1/trades/closed` | symbol?,limit,offset | List[ClosedTradeOut] |
+| `GET/PATCH /v1/config` · `PATCH /v1/agents/{id}/config` · `PATCH /v1/alerts/config` | ConfigPatch / params / AlertsConfigPatch | ConfigOut / AgentConfigOut / Dict |
+
+### 5.4 Providers de dependência (`deps.py`, `@lru_cache`)
+`get_ledger`→TradingLedger, `get_alert_store/bus`, `get_hitl_store`(wire pending_orders_provider), `get_order_store`(threshold+guardrails+alert_sink), `get_agent_registry(db_path)`, `get_exchange_client`, `get_metrics_calculator`(fresco). `_runtime_overrides:{INITIAL_CAPITAL,ORCHESTRATOR_INTERVAL_SECONDS}` (in-memory, `routes/config.py:24`).
+
+---
+
+## 6. Dados do frontend (React/JS) e contrato TS
+
+### 6.1 `apiClient.js` — CT_API (window.CT_API)
+Globais: `window.API_BASE`(default localhost:8000), `window.API_KEY`(header X-API-Key), `window.USE_MOCK_DATA`. `req()` desembrulha `j.data ?? j`. ~40 métodos mapeando 1:1 as rotas `/v1` (getMetrics, getHITL/patchHITL, getOrders/createOrder/decideOrder, getAgents/getAgentConfig/patchAgentConfig, get{Ticker,Candles,Indicators,Regime,Levels,VolumeProfile,Patterns,Signal,Confluence}, get{Protections,CircuitBreaker,Kelly,RiskConfig}/patchRiskConfig, getEquity, getProcessEvents, run{Backtest,MonteCarlo,WalkForward}/getBacktestJob, getJournal/addJournalEntry/getJournalMetrics, getConfig/patchConfig, patchAlertsConfig, getAlertHistory, subscribeAlerts=SSE). Mercado reescreve `BTC/USDT`→`BTC-USDT`.
+**CT_PAIR** store: localStorage `'ct.pair'` (default BTC/USDT); CustomEvent `'ct:pair'`. (Market usa `'ct.alerts'` para alertas de preço client-side.)
+
+### 6.2 `data.js` — CT.* (mock, quando USE_MOCK_DATA)
+~35 entidades mock espelhando os DTOs: `symbol,candles,bb,regime,indicators,sr,volumeProfile,patterns,signal,confluence,confidenceBreakdown,capital,drawdown,equity,circuitBreaker,kelly,riskConfig,guardrails,behavioral,agents(6),strategies,orders(22),pendingOrders,hitl,journal(16),journalMetrics,journalScatter,heatmap,backtest,monteCarlo,walkForward,backtestConfig,alerts(6),alertThresholds`. (Campos detalhados por `data.js:64-403`.)
+
+### 6.3 Estado por tela (screen_*.jsx) — dado → CT_API
+- **overview**: metrics(period),equity,orders — `getMetrics/getEquity/getOrders`.
+- **market**: pair,tf,candles,bb,indicators,regime,levels,volumeProfile,patterns,signal,confluence,ticker — Promise.all; `submitOrder`→OrderCreate{pair,side,quantity,price,strategy,agent_id:'manual-ui',confidence,reason,position_size_pct,stop_loss,take_profit?}.
+- **orders**: orders,statusF,sideF; mapeia mock stop→stop_loss etc.
+- **risk**: protections,cb,kelly,equity.
+- **hitl**: config,orders(pending); decide→{action,operator_note,operator_id}; setLevel→{level,reason,operator}.
+- **agents**: agents,configAgent; getAgentConfig→params, patchAgentConfig(draft).
+- **observability**: events agrupados por case_id; lê activity,timestamp,attributes{failures,duration_ms,ran}.
+- **journal**: entries,metrics; form{setup,emotion_before,stop_defined,plan_followed,pnl_pct,note}.
+- **backtest**: config,result,mc,wf,jobId; polling getBacktestJob 1500ms.
+- **settings**: sysConfig,riskConfig,alertConfig,agents; patchConfig/patchRiskConfig/patchAlertsConfig.
+- **app.jsx**: routing por hash; polling pendingCount 15s; SSE subscribeAlerts→toast (severity critical/high). AlertDrawer: getAlertHistory(50)+SSE, slice 100.
+
+### 6.4 `openapi.d.ts` — contrato transversal (TS gerado)
+34 paths + `components.schemas` com ~50 DTOs espelhando 1:1 os Pydantic (envelopes `APIResponse_*_{_links?,data,meta?}`; DTOs OrderCreate/OrderOut/PortfolioMetricsOut/IndicatorsOut/RiskConfigOut/... + enums OrderSide, OrderStatus). Gerado por `npm run gen:types`; CI barra drift (`git diff --exit-code`).
+
+---
+
+## 7. Ciclo de vida ponta-a-ponta de um dado
+
+Rastreamento de um sinal de trading, do mercado à métrica (todas as transformações do dado):
+
+1. **Exchange OHLCV** `[ts,o,h,l,c,v]` → `StrategyAgent._analyze_market` → **analysis** (TechnicalIndicators + regime + S/R + volume).
+2. analysis → `_build_market_data` → **market_data** (nested/flat) → `strategy.analyze` → resultado da estratégia (`entry,stop_loss,take_profit,position_size_pct,confidence`).
+3. `_generate_signal` normaliza → **signal** `{action,entry_price,stop_loss,take_profit,position_size_pct,strategy,regime,market_context}`; orquestrador adiciona `symbol`.
+4. **ledger** `log_signal{agent,signal}` (P). Gate de confiança <0.6 aborta.
+5. signal → `RiskAgent.execute` → guardrails.validate_order(signal) → **validation** `{approved,issues,warnings,confidence}`; `log_validation` (P). Rejeição → **Alert** `risk_rejection` (P + SSE).
+6. signal → `make_approval_handler` mapeia para **Order** (nota: `entry_price`→`price`, deriva `quantity` de `position_size_pct`). `OrderStore.submit` → INSERT `orders` (P) + XES `order_submitted`.
+7. Se `notional≤threshold` → auto-fill (`order_fill` P); senão `wait_for_decision` (polling SQLite 2s). API `PATCH` grava `approved` (P). `log_hitl_approval` (P).
+8. signal + quantity → `ExecutionAgent.execute` → exchange.create_order (paper) → **execution_result** `{order_id,executed_price,fee}`; `log_execution` + `log_fill{order_id,symbol,side,price,quantity,notional,fee}` (P); `_open_positions[order_id]` (P via PositionStore); `mark_filled` → `filled` (P).
+9. Ciclo futuro: `_check_open_positions` detecta SL/TP → `log_position_closed{...,pnl,pnl_pct}` (P); `circuit_breaker.record_trade_result(pnl_pct)` (P em circuit_breaker_state).
+10. `PortfolioMetricsCalculator.compute` relê `position_closed`+`order_fill` → **PortfolioMetrics** → `GET /v1/metrics` → **APIResponse[PortfolioMetricsOut]** → CT_API desembrulha `data` → tela overview.
+
+Em paralelo: cada ciclo emite XES `agent_cycle_started/completed/failed` → `GET /v1/process/events` → tela observability (process mining).
+
+---
+
+## 8. Dados "perdidos na classe" / declarados-mas-não-usados
+
+Dados atribuídos e **nunca lidos depois** (marca **X**), conforme pedido:
+
+| Dado | Local | Observação |
+|---|---|---|
+| `RiskAgent.max_daily_loss_pct` | `risk_agent.py:22` | lido do env, nunca referenciado na classe |
+| `UnifiedOrchestrator.sandbox` | `unified_orchestrator.py:38` | instanciado, nunca usado |
+| `UnifiedOrchestrator.chain_manager` | `unified_orchestrator.py:39` | instanciado, nunca usado |
+| `AgentMemorySystem.short_term` | `agent_memory.py:21` | dict criado, nunca escrito |
+| `SecureToolSandbox.memory_limit_mb` | `secure_executor.py:26` | declarado, não aplicado na execução |
+| `SecureToolSandbox.cpu_quota` | `secure_executor.py:27` | declarado, não aplicado |
+| `ContinuousEvaluator.baseline` (continuous_eval) | `continuous_eval.py:11` | atribuído None, nunca usado |
+| metrics `user_satisfaction`,`error_rate`,`response_time_p95` | `continuous_evaluator.py:12` | chaves declaradas, nunca alimentadas |
+| `signal["market_context"]` | `strategy_agent.py:232` | construído mas o pipeline nunca lê (RiskAgent passa o signal inteiro) |
+| `analysis["fibonacci_levels"]` | `strategy_agent.py:126` | computado, só carregado/sanitizado, nunca consumido a jusante |
+| `strategy_result.{analysis,llm_used,llm_thesis,reasoning}` | `strategy_agent.py:89` | retornados; orquestrador só lê signal/confidence/stub_used |
+| `OrchestratorLoop.order_store` | `orchestrator_loop.py:220` | exposto "para inspeção/futuro" |
+
+Dados **carregados mas subutilizados**: `KellyCriterion`/`PositionSizer`/`CapitalProtections` existem completos mas o pipeline dimensiona por `position_size_pct` simples (o Kelly só aparece em `GET /v1/risk/kelly`).
+
+---
+
+## 9. Anomalias e inconsistências de dados
+
+Achados relevantes para refatoramento (não alteram código — apenas documentados):
+
+1. **Dois formatos de `market_data`** (§3.4): `_build_market_data` do backtest emite o formato **flat** sem `indicators`; se Grid/MeanReversion forem usadas no backtest, `indicators=None` — só DCA consome o flat plenamente (`engine.py:234` vs `strategy_agent.py:383`).
+2. **Ponte de nome `entry`→`entry_price`**: estratégias emitem `entry` (`mean_reversion.py:68`), mas guardrails/Order esperam `entry_price` — o `StrategyAgent._generate_signal` faz a normalização; um consumo direto do output da estratégia quebraria a validação.
+3. **Mismatch de chave no registry de estratégias**: `STRATEGY_REGISTRY` tem `"mean_reversion"` (`__init__.py:23`) mas `_REGIME_STRATEGY_MAP` só emite `"dca"`/`"grid"` — **mean_reversion nunca é selecionada** por regime.
+4. **`ab_tests.jsonl` não é JSON válido**: gravado via `str(payload)` (repr Python), não `json.dumps` (`ab_testing.py:60`).
+5. **Colisões de nome de tipo** (mesmos dados, classes distintas): `ContinuousEvaluator`×2, `AdaptivePlanner`×2, `MemoryStore`×2, `SquadOrchestrator`×2, `Guardrail`×2.
+6. **Duas políticas de validação de ordem** com campos diferentes: `GuardrailSystem` lê `market_context/action/entry_price`; `SecurityConfig.validate_order` lê `notes/exchange/position_size_pct`.
+7. **`CandleOut` usa `lo`** (não `l`) para low (`schemas.py:216`) — divergência de convenção vs OHLCV interno `[o,h,l,c]`.
+8. **Namespace `/v1/agents/{id}/config`** servido por dois routers (`agents` GET, `config` PATCH).
+
+---
+
+> **Documento gerado por análise estática dos scripts do repositório.** Referências `arquivo:linha` preservam rastreabilidade. Nenhum código foi alterado. Complementa `docs/uml/arquitetura-uml.md` e `docs/architecture/arquitetura.md`.
diff --git a/docs/uml/arquitetura-uml.md b/docs/uml/arquitetura-uml.md
new file mode 100644
index 0000000..8d03652
--- /dev/null
+++ b/docs/uml/arquitetura-uml.md
@@ -0,0 +1,1330 @@
+# Documentação UML — Criptotrade
+
+> Análise arquitetural completa da plataforma **Criptotrade** (crypto AI trading com Human-in-the-Loop), gerada a partir da leitura recursiva de todos os arquivos-fonte do repositório.
+>
+> **Objetivo:** mapear como cada script/módulo/pacote se conecta ao todo — dependências, fluxos de dados e responsabilidades — como base para estudo de arquitetura e futuros refatoramentos.
+
+---
+
+## 0. Nota sobre o escopo multi-linguagem
+
+O prompt original pedia análise de **Python + Rust + TypeScript**. A composição real do repositório, medida por varredura recursiva, é:
+
+| Linguagem / tipo | Arquivos | Situação |
+|---|---:|---|
+| **Python** (`.py`) | 168 (~18,7k LOC) | **Núcleo do sistema** — backend, agentes, orquestração, API, análise, backtest |
+| **React/JSX** (`.jsx`, `.js`, `.mjs`) | ~24 | Console operacional em `docs/design/pages/` (React "classic scripts" + build esbuild) |
+| **TypeScript** (`.d.ts`) | 1 | `docs/design/pages/openapi.d.ts` — **contrato gerado** a partir do OpenAPI da FastAPI |
+| **Rust** (`.rs`) | 0 | **Ausente** — não há código Rust no repositório |
+| SQL / YAML / Dockerfile / conf | ~30 | Migrations, config, infra |
+
+**Decisões de adaptação:**
+
+- **Rust:** a seção específica é omitida por inexistência de código Rust. A metodologia de ownership/traits não se aplica.
+- **TypeScript:** existe um único arquivo `.d.ts`, que **não é código de aplicação** e sim o **contrato transversal** gerado (`openapi-typescript` a partir do `openapi.json` emitido pela FastAPI). Ele é tratado na seção de *entidades transversais* (§ 4 e § 5.8), por ser exatamente o elo tipado entre backend Python e frontend JS.
+- **Frontend:** o console React (JSX) é o equivalente prático da camada "TypeScript/front-end" da metodologia — é analisado como componente e pacote.
+
+O restante deste documento segue a metodologia pedida (mapeamento estrutural → análise estática → padrões → diagramas), aplicada à realidade Python-cêntrica do projeto.
+
+---
+
+## 1. Mapeamento Estrutural (Top-down)
+
+### 1.1 Mapa de diretórios e responsabilidades
+
+```
+Criptotrade/
+├── src/ # ── BACKEND (Python) ──────────────────────
+│ ├── agents/ # Agentes de IA (BaseAgent + especialistas)
+│ ├── orchestration/ # Loop contínuo + SquadOrchestrator (pipeline de trading)
+│ ├── strategies/ # Estratégias de trading (Strategy pattern)
+│ ├── analysis/ # Indicadores, S/R, volume, regime, padrões
+│ ├── backtest/ # Engine, Monte Carlo, walk-forward
+│ ├── evaluation/ # Avaliação contínua, A/B testing
+│ ├── consensus/ # Votação ponderada entre agentes
+│ ├── routing/ # Roteamento adaptativo (learning router)
+│ ├── planning/ # Planejamento hierárquico/adaptativo
+│ ├── chains/ · parallel/ # Cadeias resilientes · execução paralela
+│ ├── risk/ # Kelly, sizing, proteções de capital
+│ ├── safety/ # GuardrailSystem, SecurityConfig
+│ ├── hitl/ # Human-in-the-Loop (OrderStore, níveis de autonomia)
+│ ├── core/ # Infra: config, db, ledger, métricas, exchange, alerts, llm
+│ ├── memory/ # Memória de agentes (JSONL + Chroma opcional)
+│ ├── journal/ # Diário de trades (psicologia/plano)
+│ ├── tools/ # MCP registry, RAG, sandbox seguro
+│ ├── protocols/ # MCP server, squad (variante A2A)
+│ ├── api/ # FastAPI gateway /v1 (routes, schemas, deps, middleware)
+│ ├── dashboard/ # Console Streamlit (consumidor da API)
+│ └── utils/ # Observabilidade, memória utilitária
+├── docs/design/pages/ # ── FRONTEND (React/JSX) + openapi.d.ts (TS) ──
+├── migrations/ # DDL SQLite/Postgres
+├── config/ · monitoring/ # YAML de risco/prompts · Prometheus/Grafana
+├── deploy/ · docker-compose*.yml# nginx, Dockerfiles, topologias dev/prod/vps
+└── tests/ # unit · integration · api · hitl · emergent
+```
+
+### 1.2 Fronteiras de sistema e mecanismos de comunicação
+
+O sistema roda em **dois processos que não compartilham lifecycle** (um restart da API não para o trading), comunicando-se por **estado compartilhado em SQLite (WAL)** + volume de arquivos append-only:
+
+```
+Browser / Operador
+ │ HTTPS (REST + SSE)
+ ▼
+[nginx] ──proxy HTTP──► [API FastAPI :8000] ◄──HTTP── [Dashboard Streamlit :8501]
+ │ escreve/lê (consumidor puro da API)
+ ▼
+ ┌───────────────────────────┐
+ │ SQLite WAL (orders, │◄── shared state (cross-process)
+ │ cycle_events, ledger_...) │
+ │ + JSONL (ledger, alerts) │
+ └───────────────────────────┘
+ ▲
+ │ escreve/lê (mesmo volume ./data)
+ [Orchestrator loop process] ── python -m src.orchestration.main_loop
+ │
+ └─► ExchangeClient (dry-run/paper por padrão)
+```
+
+| Fronteira | Mecanismo | Detalhe |
+|---|---|---|
+| Browser ↔ nginx | **HTTPS** | TLS 1.2/1.3, HSTS |
+| Console/Dashboard ↔ API | **REST `/v1` + SSE** | envelope `APIResponse`, header `X-API-Key`, SSE em `/v1/alerts` |
+| API ↔ Loop | **SQLite WAL** (sem RPC) | HITL bridge: API *decide* (`approved`/`rejected`), loop *executa* (`wait_for_decision` → `mark_filled`) |
+| Backend ↔ Exchange | **CCXT** (async) | por padrão `EXCHANGE_DRY_RUN=true` → mercado sintético offline |
+| Backend ↔ LLM | **LangChain** (opcional) | Gemini/OpenAI/Anthropic; ausência de chave = desabilitado (fail-safe) |
+| Backend Python ↔ Frontend JS | **`openapi.d.ts`** (contrato) | gerado do OpenAPI; drift barrado no CI (`git diff --exit-code`) |
+| Métricas | **Prometheus scrape** | `/metrics`; Grafana provisionado |
+
+### 1.3 Dependências externas por linguagem
+
+**Python (`requirements.txt`):**
+- Núcleo: `pydantic`, `pydantic-settings`, `PyYAML`, `python-dotenv`
+- API/Web: `fastapi`, `uvicorn`, `streamlit`, `httpx`, `sse-starlette`, `slowapi`
+- IA/Agentes: `langchain`, `langchain-core`, `langchain-google-genai` (Chroma opcional)
+- Trading/dados: `ccxt`, `pandas`, `numpy`, `ta`
+- Persistência: `sqlalchemy`, `psycopg[binary]` (Postgres opcional)
+- Observabilidade: `prometheus-client`, `python-json-logger`, `sentry-sdk[fastapi]`
+- Segurança: `cryptography`, `slowapi`; async: `aiohttp`
+
+**Frontend (`docs/design/pages/package.json`):**
+- Runtime: `react` / `react-dom` 18.3.1 (UMD globals, sem bundler em dev)
+- Build/test: `esbuild`, `openapi-typescript`, `@playwright/test`, `typescript`
+
+---
+
+## 2. Diagrama de Casos de Uso
+
+**Objetivo:** identificar atores e as funcionalidades principais visíveis nas fronteiras do sistema (API `/v1` + dashboard + loop).
+
+**Escopo:** operação do sistema pelo operador humano (HITL), automação do loop de trading, e integrações externas (exchange, LLM, monitoramento).
+
+```plantuml
+@startuml
+left to right direction
+skinparam actorStyle awesome
+
+actor "Operador\n(Trader)" as Op
+actor "Sistema\nOrquestrador\n(Loop)" as Loop
+actor "Exchange\n(CCXT)" as Ex
+actor "LLM\n(Gemini/OpenAI)" as LLM
+actor "Prometheus\n/Grafana" as Mon
+
+rectangle "Criptotrade" {
+ usecase "Aprovar/Rejeitar ordem (HITL)" as UC_HITL
+ usecase "Ajustar nível de autonomia" as UC_Auto
+ usecase "Visualizar KPIs do portfólio" as UC_KPI
+ usecase "Consultar agentes e ciclos" as UC_Agents
+ usecase "Analisar mercado (indicadores/regime)" as UC_Market
+ usecase "Rodar backtest / Monte Carlo / walk-forward" as UC_BT
+ usecase "Registrar/consultar diário de trades" as UC_Journal
+ usecase "Receber alertas (SSE)" as UC_Alerts
+ usecase "Ajustar parâmetros de risco" as UC_Risk
+
+ usecase "Gerar sinal de trading" as UC_Signal
+ usecase "Validar risco (guardrails)" as UC_Validate
+ usecase "Executar ordem (paper)" as UC_Exec
+ usecase "Auditar via ledger/XES" as UC_Audit
+ usecase "Disparar circuit breaker" as UC_CB
+}
+
+Op --> UC_HITL
+Op --> UC_Auto
+Op --> UC_KPI
+Op --> UC_Agents
+Op --> UC_Market
+Op --> UC_BT
+Op --> UC_Journal
+Op --> UC_Alerts
+Op --> UC_Risk
+
+Loop --> UC_Signal
+Loop --> UC_Validate
+Loop --> UC_Exec
+
+UC_Signal ..> UC_Market : «include»
+UC_Signal ..> LLM : «include» (CoT opcional)
+UC_Validate ..> UC_CB : «extend» (perda/streak)
+UC_Exec ..> UC_HITL : «include» (gate humano)
+UC_Exec ..> Ex : «include»
+UC_Signal ..> UC_Audit : «include»
+UC_Validate ..> UC_Audit : «include»
+UC_Exec ..> UC_Audit : «include»
+UC_Validate ..> UC_Alerts : «extend» (rejeição)
+
+Ex --> UC_Market
+LLM --> UC_Signal
+Mon --> UC_KPI
+@enduml
+```
+
+**Legenda:** `«include»` = comportamento sempre executado como parte do caso base; `«extend»` = comportamento condicional; atores à esquerda (humano/sistemas) disparam os casos.
+
+**Notas de design:**
+- O **gate HITL** é obrigatório na execução (`«include»`), mas o modo *auto-approval* (Model B) fila-e-preenche ordens abaixo do limiar de valor sem intervenção — o operador só é chamado acima do threshold.
+- O **circuit breaker** é uma extensão do fluxo de validação/resultado: dispara com perda diária ≥ 4% ou 3 perdas consecutivas.
+- O **LLM é opcional**: Chain-of-Thought no `StrategyAgent` e reflexão no `RiskAgent` degradam graciosamente para o score determinístico quando não há chave de API.
+
+---
+
+## 3. Diagrama de Pacotes
+
+**Objetivo:** organização lógica do código Python e dependências direcionadas entre pacotes.
+
+**Escopo:** todos os subpacotes de `src/` + frontend + contrato.
+
+```mermaid
+graph TD
+ subgraph Apresentacao["Apresentação"]
+ API[api
FastAPI /v1]
+ DASH[dashboard
Streamlit]
+ FE[docs/design/pages
React console]
+ end
+ subgraph Aplicacao["Aplicação / Orquestração"]
+ ORCH[orchestration]
+ HITL[hitl]
+ CONS[consensus]
+ ROUT[routing]
+ PLAN[planning]
+ CHAIN[chains]
+ PAR[parallel]
+ EVAL[evaluation]
+ end
+ subgraph Dominio["Domínio"]
+ AG[agents]
+ STRAT[strategies]
+ ANAL[analysis]
+ BT[backtest]
+ RISK[risk]
+ JOUR[journal]
+ end
+ subgraph Infra["Infraestrutura"]
+ CORE[core
config·db·ledger·metrics·exchange·alerts·llm]
+ SAFE[safety]
+ MEM[memory]
+ TOOLS[tools]
+ PROTO[protocols]
+ UTIL[utils]
+ end
+ CONTRACT[["openapi.d.ts
(contrato TS gerado)"]]
+
+ FE -->|REST+SSE| API
+ DASH -->|httpx| API
+ API --> CONTRACT
+ FE -.consome.-> CONTRACT
+
+ API --> HITL
+ API --> AG
+ API --> CORE
+ API --> ANAL
+ API --> BT
+ API --> RISK
+
+ ORCH --> AG
+ ORCH --> HITL
+ ORCH --> CORE
+ HITL --> SAFE
+ HITL --> CORE
+
+ AG --> ANAL
+ AG --> STRAT
+ AG --> SAFE
+ AG --> CORE
+ AG --> MEM
+
+ STRAT --> ANAL
+ BT --> STRAT
+ RISK --> CORE
+ JOUR --> CORE
+
+ ORCH --> PLAN
+ ORCH --> ROUT
+ ORCH --> CONS
+ ORCH --> CHAIN
+ ORCH --> PAR
+ ORCH --> EVAL
+
+ CORE --> SAFE
+ TOOLS --> SAFE
+ PROTO --> AG
+```
+
+**Legenda:** seta cheia = dependência de import/uso; seta tracejada = consumo do contrato; caixa `[[...]]` = artefato gerado. Camadas de cima (apresentação) dependem das de baixo (infra), não o contrário.
+
+**Notas de design:**
+- **Direção de dependência saudável no eixo principal:** apresentação → aplicação → domínio → infra. `core` é o núcleo estável do qual quase tudo depende (config, db, ledger).
+- **Dois "clusters" de orquestração convivem** (ver § 5.2): o cluster de **trading** (`orchestration.squad_orchestrator` + `agents.{strategy,risk,execution}`) e o cluster **genérico "BuildToValue"** (`orchestration.unified_orchestrator` + `planning/routing/consensus/chains/parallel` + agentes de engenharia). O segundo **não está no caminho de trading** — é acoplamento potencialmente removível.
+- **Acoplamento fraco via callbacks:** `safety.guardrails` não importa `core.alerts`; recebe um `alert_sink: Callable[[str], None]`. Boa inversão de dependência.
+
+---
+
+## 4. Diagrama de Componentes
+
+**Objetivo:** componentes de runtime (processos/serviços) e interfaces de comunicação.
+
+**Escopo:** deploy lógico — API, loop, dashboard, console, persistência, atores externos.
+
+```mermaid
+graph LR
+ subgraph Browser
+ Console["React Console
«component»"]
+ end
+ Operator(("Operador"))
+
+ subgraph Edge
+ NGINX["nginx
«reverse proxy»
TLS, static, /v1 proxy"]
+ end
+
+ subgraph AppNode["Nó de aplicação (imagem única Python)"]
+ API["API FastAPI
«service» :8000
REST /v1 + SSE"]
+ LOOP["Orchestrator Loop
«process»
main_loop"]
+ DASH["Dashboard
«service» Streamlit :8501"]
+ end
+
+ subgraph State["Estado compartilhado (volume ./data)"]
+ DB[("SQLite WAL
orders · cycle_events
ledger_events")]
+ JSONL[("JSONL
ledger · alerts · XES")]
+ end
+
+ subgraph External
+ EX["Exchange (CCXT)
«external»"]
+ LLM["LLM Provider
«external»"]
+ end
+ subgraph Obs
+ PROM["Prometheus :9090"]
+ GRAF["Grafana :3000"]
+ end
+
+ Operator -->|HTTPS| Console
+ Console -->|REST+SSE| NGINX
+ NGINX -->|HTTP /v1,/health| API
+ Operator -.->|opção operacional| DASH
+ DASH -->|httpx REST| API
+
+ API -->|read/write| DB
+ API -->|read/write| JSONL
+ LOOP -->|read/write| DB
+ LOOP -->|append| JSONL
+ LOOP -->|analyze_and_trade| EX
+ API -->|market data| EX
+ LOOP -.->|CoT/reflection opcional| LLM
+ API -.->|CoT opcional| LLM
+
+ PROM -->|scrape /metrics| API
+ GRAF -->|query| PROM
+```
+
+**Legenda:** `«service»` = processo servido em porta; `«process»` = processo sem porta; `«external»` = dependência fora do deploy; cilindro = repositório de dados; seta tracejada = dependência opcional.
+
+**Notas de design:**
+- **Uma única imagem Python** serve API, loop e dashboard (diferem só pelo `command`). Simplifica build; separa lifecycle por processo.
+- **Ponto de integração crítico (e frágil):** a coordenação API↔loop é feita **exclusivamente via polling do SQLite** (`wait_for_decision`, `poll_interval=2s`). Não há fila/mensageria — é simples e cross-process, mas o loop só percebe uma aprovação com latência de até `poll_interval`, e um timeout de 300s auto-cancela (fail-closed).
+- **Console é 100% estático** (React "classic scripts" transpilado por esbuild, servido por nginx). Não há servidor Node em produção.
+
+---
+
+## 5. Diagramas de Classes (por camada)
+
+> Dado o volume (>150 classes), o diagrama de classes é **quebrado por camada** com referência cruzada, conforme o critério de clareza da metodologia.
+
+### Legenda comum (todas as subseções)
+
+| Notação | Significado |
+|---|---|
+| `<|--` | herança / implementação |
+| `*--` | composição (dono cria/possui) |
+| `o--` | agregação (referência injetada) |
+| `-->` | associação / uso |
+| `..>` | dependência pontual (import lazy, factory) |
+| `«abstract»` `«dataclass»` `«enum»` `«protocol»` `«service»` `«repository»` `«dto»` `«factory»` | estereótipos |
+
+---
+
+### 5.1 Camada de Agentes
+
+**Objetivo:** hierarquia dos agentes e suas dependências para o domínio de análise/risco/execução.
+**Escopo:** `src/agents/*` + `src/core/safe_agent_base.py`.
+
+```mermaid
+classDiagram
+ class BaseAgent {
+ <>
+ +agent_type: str
+ +agent_id: str
+ +confidence_threshold: float = 0.6
+ +memory: AgentMemorySystem?
+ +tools: list~str~
+ +execute(task) dict*
+ +validate_input(task) bool
+ +log_decision(decision) dict
+ +validate_confidence(c) bool
+ }
+ class StrategyAgent {
+ +exchange_client
+ -_sr_detector: SupportResistanceDetector
+ -_div_detector: DivergenceDetector
+ -_llm
+ +execute(task) dict
+ }
+ class RiskAgent {
+ +guardrails: GuardrailSystem
+ +max_position_size_pct
+ +stop_loss_pct
+ +execute(task) dict
+ }
+ class ExecutionAgent {
+ +exchange: ExchangeClient
+ +paper_trading = True
+ +execute(task) dict
+ }
+ class AuditorAgent
+ class ArchitectAgent
+ class DeveloperAgent
+ class DesignerAgent
+ class OpsAgent
+
+ BaseAgent <|-- StrategyAgent
+ BaseAgent <|-- RiskAgent
+ BaseAgent <|-- ExecutionAgent
+ BaseAgent <|-- AuditorAgent
+ BaseAgent <|-- ArchitectAgent
+ BaseAgent <|-- DeveloperAgent
+ BaseAgent <|-- DesignerAgent
+ BaseAgent <|-- OpsAgent
+
+ class Agent {
+ <>
+ +name: str
+ +arun(task) str
+ }
+ class SupervisorAgent {
+ <>
+ +orchestrator: Agent
+ +specialists: list~Agent~
+ +run(task) str
+ }
+ class RecoveryAgent {
+ <>
+ +remediation_tool
+ +arun(report) str
+ }
+ class ExplorationAgent {
+ <>
+ +scanner_tool
+ +arun(instruction) str
+ }
+ SupervisorAgent o-- Agent
+ Agent <|.. RecoveryAgent
+ Agent <|.. ExplorationAgent
+
+ class AgentRegistry {
+ <>
+ -_db_path
+ +record_cycle(agent_id)
+ +cycles_today(agent_id) int
+ +status(agent_id) dict
+ +all_statuses() list
+ }
+ class AgentInfo {
+ <>
+ +id · domain · implemented · description
+ }
+ AgentRegistry --> AgentInfo
+
+ class BehavioralGuard {
+ +check(trade, history) BehavioralAlert
+ }
+ class BehavioralAlert { <> }
+ BehavioralGuard --> BehavioralAlert
+
+ StrategyAgent ..> GuardrailSystem
+ RiskAgent *-- GuardrailSystem
+ ExecutionAgent o-- ExchangeClient
+ BaseAgent ..> AgentMemorySystem
+```
+
+**Notas de design:**
+- **Padrão Template Method + async:** `BaseAgent.execute` é `@abstractmethod async`; cada especialista implementa seu raciocínio (CoT no Strategy, Reflection no Risk, ReAct no Execution).
+- **Duas hierarquias de "agente base" desconectadas:** `BaseAgent` (ABC, `execute` async, em `src/agents/`) e `SafeAgentBase` (em `src/core/`, `execute` **síncrono**, composição de guardrails — ver § 5.6). **Nenhum arquivo faz a ponte** — é um sintoma de duas fundações concebidas em momentos diferentes; candidato a unificação.
+- **Registry não é factory:** `AgentRegistry` só reporta metadados/status/ciclos (cross-process via `cycle_events`); não instancia agentes. Apenas `strategy`/`risk`/`execution` são `implemented=True`; `recovery`/`exploration` são stubs (`implemented=False` → API retorna 501). Os agentes de engenharia (architect/auditor/…) são subclasses de `BaseAgent` **não registradas** no domínio de trading.
+- **Protocolo estrutural:** `SupervisorAgent` depende do `Agent` Protocol; `Recovery/Exploration` o satisfazem (duck typing), embora ainda não estejam ligados ao trading.
+
+---
+
+### 5.2 Camada de Orquestração e Pipeline de Trading
+
+**Objetivo:** o coração do sistema — como o loop dirige o `SquadOrchestrator` e como este encadeia os agentes.
+**Escopo:** `src/orchestration/*`.
+
+```mermaid
+classDiagram
+ class OrchestratorLoop {
+ <>
+ +interval: int
+ +symbols: list~str~
+ -_stop_event: asyncio.Event
+ +run_cycle() dict
+ +run_forever()
+ +stop()
+ +from_env() OrchestratorLoop$
+ }
+ class SquadOrchestrator {
+ <>
+ +strategy_agent: StrategyAgent
+ +risk_agent: RiskAgent
+ +execution_agent: ExecutionAgent
+ +ledger: TradingLedger
+ +circuit_breaker: CircuitBreaker
+ +approval_handler
+ +fill_callback
+ -_open_positions: dict
+ +analyze_and_trade(symbol, tf) dict
+ +reload_open_positions()
+ }
+ class CircuitBreaker {
+ +DAILY_LOSS_LIMIT_PCT = 4.0
+ +CONSECUTIVE_LOSS_LIMIT = 3
+ +is_open: bool
+ +record_trade_result(pnl_pct)
+ +reset_daily()
+ }
+ class PositionStore {
+ <>
+ +upsert(id, pos)
+ +delete(id)
+ +load_all() dict
+ }
+ class AgentRegistry
+ class TradingLedger
+
+ OrchestratorLoop o-- SquadOrchestrator
+ OrchestratorLoop o-- AgentRegistry
+ OrchestratorLoop o-- TradingLedger
+ SquadOrchestrator *-- CircuitBreaker
+ SquadOrchestrator *-- PositionStore
+ SquadOrchestrator *-- StrategyAgent
+ SquadOrchestrator *-- RiskAgent
+ SquadOrchestrator *-- ExecutionAgent
+ SquadOrchestrator *-- TradingLedger
+ SquadOrchestrator ..> OrderStore : approval_handler
+ CircuitBreaker --> TradingLedger
+
+ class UnifiedOrchestrator {
+ <>
+ +planner · router · consensus
+ +parallel · evaluator · autonomy
+ +agents: dict
+ +execute_complex_task(task) dict
+ }
+ class AdaptivePlanner
+ class LearningRouter
+ class WeightedConsensusEngine
+ class ParallelResourceManager
+ class ContinuousEvaluator
+ class ProgressiveAutonomyManager
+ UnifiedOrchestrator *-- AdaptivePlanner
+ UnifiedOrchestrator *-- LearningRouter
+ UnifiedOrchestrator *-- WeightedConsensusEngine
+ UnifiedOrchestrator *-- ParallelResourceManager
+ UnifiedOrchestrator *-- ContinuousEvaluator
+ UnifiedOrchestrator *-- ProgressiveAutonomyManager
+```
+
+**Notas de design:**
+- **Pipeline central (`SquadOrchestrator.analyze_and_trade`):** `Strategy → (checagem de posições SL/TP) → Risk → Guardrails → HITL → Execution → fill/ledger`, com circuit breaker no início. Tudo `async`; helpers de sizing/PnL são síncronos. Ver sequência em § 6.1.
+- **Recuperação de restart:** `reload_open_positions()` restaura o *position book* e o estado do breaker do SQLite (evita "posições zumbi").
+- **Dois orquestradores + colisões de nome (atenção para refatoramento):**
+ - `SquadOrchestrator` existe **duas vezes** (`orchestration/` — trading real; `protocols/` — variante A2A architect+developer).
+ - `AdaptivePlanner` existe **duas vezes** (`planning/adaptive_planner.py` vs `planning/adaptive_replanner.py`).
+ - `ContinuousEvaluator` existe **duas vezes** (`evaluation/continuous_eval.py` plain vs `evaluation/continuous_evaluator.py` dataclass).
+ - O `UnifiedOrchestrator` e todo o cluster planning/routing/consensus **não são exercitados pelo trading** — é infraestrutura genérica "BuildToValue" paralela. Alto risco de código morto/confusão.
+
+---
+
+### 5.3 Camada de Estratégias e Análise
+
+**Objetivo:** o Strategy pattern e os DTOs de análise técnica que alimentam as estratégias.
+**Escopo:** `src/strategies/*`, `src/analysis/*`.
+
+```mermaid
+classDiagram
+ class BaseStrategy {
+ <>
+ +analyze(market_data) dict*
+ +get_parameters() dict
+ }
+ class MeanReversionStrategy {
+ +rsi_oversold = 30
+ +rsi_overbought = 70
+ +analyze(md) dict
+ }
+ class GridTradingStrategy {
+ +grid_levels = 10
+ +analyze(md) dict
+ }
+ class DCAOptimizedStrategy {
+ +num_entries = 3
+ +analyze(md) dict
+ }
+ BaseStrategy <|-- MeanReversionStrategy
+ BaseStrategy <|-- GridTradingStrategy
+ BaseStrategy <|-- DCAOptimizedStrategy
+
+ class STRATEGY_REGISTRY {
+ <>
+ +dca : DCAOptimizedStrategy
+ +grid : GridTradingStrategy
+ +mean_reversion : MeanReversionStrategy
+ }
+ STRATEGY_REGISTRY ..> BaseStrategy
+
+ class TechnicalAnalyzer {
+ +MIN_CANDLES = 50
+ +get_latest() TechnicalIndicators
+ +get_series(col) Series
+ }
+ class TechnicalIndicators {
+ <>
+ rsi · macd_hist · bb_percent
+ ema_fast · atr · volume_ratio · ...
+ }
+ class SupportResistanceDetector {
+ +detect(ohlcv) SRLevels
+ +fibonacci_levels() dict$
+ }
+ class SRLevels { <> }
+ class VolumeProfile { +analyze() VolumeProfileResult }
+ class VolumeProfileResult { <> +poc }
+ class DivergenceDetector { +check_rsi_price() DivergenceResult }
+ class PatternScanner { +scan(ohlcv) list~PatternResult~ }
+
+ TechnicalAnalyzer --> TechnicalIndicators
+ SupportResistanceDetector --> SRLevels
+ VolumeProfile --> VolumeProfileResult
+ MeanReversionStrategy ..> TechnicalIndicators
+ GridTradingStrategy ..> TechnicalIndicators
+ GridTradingStrategy ..> VolumeProfileResult
+
+ class regime_detector {
+ <>
+ detect_regime() str
+ strategies_for_regime() list
+ detect_market_extreme()
+ }
+ StrategyAgent ..> TechnicalAnalyzer
+ StrategyAgent ..> regime_detector
+ StrategyAgent ..> STRATEGY_REGISTRY
+ regime_detector ..> STRATEGY_REGISTRY : keys alinhadas
+```
+
+**Notas de design:**
+- **Strategy pattern completo:** `BaseStrategy` (ABC) ← 3 concretas, selecionadas via `STRATEGY_REGISTRY` (registro plugin, com import condicional). O `StrategyAgent` escolhe a estratégia pelo **regime de mercado detectado** (`regime_detector.strategies_for_regime`), cujas chaves espelham as do registry.
+- **DTO central:** `TechnicalIndicators` (`@dataclass`, ~20 campos) é produzido por `TechnicalAnalyzer` e consumido polimorficamente pelas estratégias via `market_data["indicators"]`. Bom desacoplamento por dados.
+- **Consumo duck-typed:** `BacktestEngine` (§ 5.4) também chama `strategy.analyze(...)`, tratando qualquer `BaseStrategy` como *context* — reuso limpo entre trading e backtest.
+- **`regime_detector` é módulo de funções** (sem classe) — ponte funcional entre análise e o factory de estratégias.
+
+---
+
+### 5.4 Camada de Backtest e Avaliação
+
+**Objetivo:** validação de estratégias fora do caminho de produção.
+**Escopo:** `src/backtest/*`, `src/evaluation/*`.
+
+```mermaid
+classDiagram
+ class BacktestEngine {
+ +initial_capital = 10_000
+ +commission_pct · slippage_pct
+ +run(strategy, ohlcv) BacktestResult
+ }
+ class BacktestResult {
+ <>
+ +total_trades · win_rate
+ +sharpe_ratio · max_drawdown_pct
+ +trades: list~BacktestTrade~
+ +expectancy: float
+ }
+ class BacktestTrade { <> }
+ class WalkForwardValidator {
+ +window_size = 252
+ +validate(strategy, ohlcv) WalkForwardResult
+ }
+ class WalkForwardResult { <> +window_results }
+ class WindowResult { <> }
+ class MonteCarloSimulator {
+ +n_simulations = 1000
+ +simulate(pnl_list) MonteCarloResult
+ }
+ class MonteCarloResult { <> +rejected }
+
+ BacktestEngine --> BacktestResult
+ BacktestResult *-- BacktestTrade
+ WalkForwardValidator ..> BacktestEngine
+ WalkForwardValidator --> WalkForwardResult
+ WalkForwardResult *-- WindowResult
+ WindowResult *-- BacktestResult
+ MonteCarloSimulator --> MonteCarloResult
+ BacktestEngine ..> BaseStrategy : duck-typed analyze()
+
+ class AgentABTestingFramework { <> +run_ab_test() }
+ class ContinuousEvaluator_eval { <> +evaluate_trajectory() }
+ class ContinuousEvaluator_dc { <> +evaluate_agent_performance() }
+ note for ContinuousEvaluator_dc "Colisão de nome:\ncontinuous_eval.py vs\ncontinuous_evaluator.py"
+```
+
+**Notas de design:**
+- **Composição em árvore de resultados:** `WalkForwardResult ◆ WindowResult ◆ BacktestResult ◆ BacktestTrade` — DTOs imutáveis, fáceis de serializar para a API (`/v1/backtest/*`).
+- **Validação anti-overfitting:** o `WalkForwardValidator` (`MAX_SHARPE_DEVIATION=0.30`) e o `MonteCarloSimulator` (percentis 5/95, `rejected`) formam um filtro de robustez estatística antes de promover uma estratégia.
+- **`ContinuousEvaluator` duplicado:** duas classes homônimas com contratos diferentes — só a versão dataclass é usada pelo `UnifiedOrchestrator`. Consolidar.
+
+---
+
+### 5.5 Camada Core — Persistência, Métricas e Integrações
+
+**Objetivo:** a "espinha dorsal" de infraestrutura (ledger append-only, abstração de DB, métricas, exchange, alertas, LLM).
+**Escopo:** `src/core/*`.
+
+```mermaid
+classDiagram
+ class Settings {
+ <>
+ app_env · exchange · initial_capital
+ max_position_size_pct · autonomy_level ...
+ }
+ class TradingLedger {
+ <>
+ +ledger_path · db_path
+ +log_signal()
+ +log_validation()
+ +log_execution()
+ +log_hitl_approval()
+ +log_process_event() XES
+ +log_fill()
+ +log_position_closed()
+ +get_events(type) list
+ +get_process_events(case) list
+ }
+ class PortfolioMetricsCalculator {
+ <>
+ +compute(period, symbol) PortfolioMetrics
+ }
+ class PortfolioMetrics { <> +sharpe · win_rate · drawdown }
+ class ExchangeClient {
+ <>
+ +dry_run: bool
+ +fetch_ohlcv()
+ +fetch_ticker()
+ +create_order()
+ +_create_paper_order()
+ }
+ class Alert { <> +severity · type · message }
+ class AlertStore {
+ <>
+ +append()
+ +history()
+ }
+ class AlertBus {
+ +publish()
+ +register() Queue
+ }
+ class LLMClient {
+ <>
+ +reason(sys, user) str?
+ +reason_json(sys, user) dict?
+ }
+ class db {
+ <>
+ +connection() ctx
+ +init_db()
+ +upsert()
+ +is_postgres() bool
+ +autoincrement_pk() str
+ }
+ class RateLimiter { <> +allow(key, limit) bool }
+ class InMemoryRateLimiter
+ class RedisFixedWindowLimiter
+ RateLimiter <|.. InMemoryRateLimiter
+ RateLimiter <|.. RedisFixedWindowLimiter
+
+ TradingLedger ..> db
+ PortfolioMetricsCalculator o-- TradingLedger
+ PortfolioMetricsCalculator --> PortfolioMetrics
+ ExchangeClient ..> synthetic_market
+ AlertStore --> Alert
+ AlertBus --> Alert
+ Settings ..> RequestIdLogFilter
+```
+
+**Notas de design:**
+- **Ledger como *event store* (Event Sourcing leve):** `TradingLedger` grava eventos append-only em SQLite (`ledger_events`) e emite um **event log XES** (`log_process_event`) para *process mining* (`/v1/process/events`). O `PortfolioMetricsCalculator` **deriva** todas as métricas relendo esses eventos (`position_closed`, `order_fill`) — fonte única de verdade, sem estado mutável duplicado.
+- **Abstração de DB portável:** `db.connection()` é o único ponto de acesso; `_PgConn`/`_Row` adaptam Postgres à API do `sqlite3`. Migrations são backend-aware (`migrations/` + `migrations/postgres/`). Permite escalar de SQLite (default) a Postgres (ADR-005) sem tocar nos repositórios.
+- **Fail-safe por design:** `ExchangeClient` exige `EXCHANGE_DRY_RUN` (recusa iniciar sem) e, em dry-run, usa `synthetic_market` offline. `LLMClient` retorna `None` sem chave. `RedisFixedWindowLimiter` faz *fail-open* para memória.
+- **Pub/Sub para SSE:** `AlertBus` faz fan-out in-process para `asyncio.Queue` (assinantes do `/v1/alerts`); `AlertStore` persiste em JSONL. `make_guardrail_sink` conecta guardrails → alertas por callback (sem import cruzado).
+
+---
+
+### 5.6 Camada de Risco e Segurança
+
+**Objetivo:** as regras que protegem capital e execução.
+**Escopo:** `src/risk/*`, `src/safety/*`, `src/core/safe_agent_base.py`.
+
+```mermaid
+classDiagram
+ class GuardrailSystem {
+ <>
+ +rules: list~Guardrail~
+ +alert_sink: Callable?
+ +validate_order(order) tuple~bool,list~
+ +check_position_size()
+ +check_stop_loss()
+ +check_risk_reward()
+ +check_market_conditions()
+ }
+ class SecurityConfig {
+ <>
+ +MAX_POSITION_SIZE_PCT = 5.0
+ +FORBIDDEN_PATTERNS
+ +validate_order() tuple$
+ +validate_tool_call() tuple$
+ }
+ class CapitalProtections {
+ +DAILY_LIMIT_PCT = 3.0
+ +WEEKLY_LIMIT_PCT = 6.0
+ +check(daily, weekly, monthly) ProtectionResult
+ }
+ class ProtectionResult { <> +status +size_multiplier +can_trade }
+ class DrawdownStatus { <> OK·WARN·DAILY_PAUSE·... }
+ class KellyCriterion {
+ <>
+ +full_kelly() float?
+ +fractional_kelly() float
+ +ruin_risk() float
+ }
+ class PositionSizer {
+ +kelly: KellyCriterion?
+ +compute(entry, stop) float
+ }
+ CapitalProtections --> ProtectionResult
+ ProtectionResult --> DrawdownStatus
+ PositionSizer o-- KellyCriterion
+ RiskAgent *-- GuardrailSystem
+ GuardrailSystem ..> AlertStore : alert_sink
+
+ class SafeAgentBase {
+ +guardrails: GuardrailSuite
+ +memory: MemoryManager
+ +execute(task, ctx) AgentExecution
+ }
+ class GuardrailSuite { <> +validate_pattern_safety() }
+ class Guardrail_proto { <> +validate(pattern) bool }
+ class InputSanitizer
+ class OutputValidator
+ class EthicalBoundaryChecker
+ class ResourceLimiter
+ SafeAgentBase *-- GuardrailSuite
+ GuardrailSuite o-- Guardrail_proto
+ Guardrail_proto <|.. InputSanitizer
+ Guardrail_proto <|.. OutputValidator
+ Guardrail_proto <|.. EthicalBoundaryChecker
+ Guardrail_proto <|.. ResourceLimiter
+```
+
+**Notas de design:**
+- **Dois conceitos de "guardrail" coexistem** (colisão semântica a esclarecer): (1) `safety.guardrails.GuardrailSystem` — regras de **risco de trading** por ordem (position size, stop, risk-reward), usado pelo `RiskAgent` e pelo `OrderStore`; (2) `core.safe_agent_base` — **guardrails de segurança de execução** (sanitização, ética, limites de recurso) sobre o `SafeAgentBase` (que não é usado no trading). Nomes iguais, propósitos distintos.
+- **Duas validações de ordem redundantes:** `GuardrailSystem.validate_order` (instância, com regras dinâmicas + alertas) e `SecurityConfig.validate_order` (classmethod estático). Convém eleger uma única fonte de política.
+- **Sizing avançado disponível, mas subutilizado:** `KellyCriterion`/`PositionSizer`/`CapitalProtections` são módulos ricos e testáveis, porém o `SquadOrchestrator` ainda dimensiona posição por `position_size_pct` simples — oportunidade de plugar o Kelly no pipeline (o endpoint `/v1/risk/kelly` já o expõe).
+
+---
+
+### 5.7 Camada HITL, DTOs da API e Contrato
+
+**Objetivo:** o gate humano cross-process e o contrato de dados da API.
+**Escopo:** `src/hitl/*`, `src/api/schemas.py`, `openapi.d.ts`.
+
+```mermaid
+classDiagram
+ class OrderStore {
+ <>
+ +submit(order) Order
+ +resolve(id, approved, op) Order
+ +mark_filled(id) Order
+ +cancel(id, reason) Order
+ +wait_for_decision(id, timeout) bool
+ +list()
+ +count()
+ +pending_count()
+ }
+ class Order {
+ <>
+ +id · pair · side · quantity · price
+ +status: OrderStatus
+ +auto_approved · critical
+ +notional: float
+ +guardrail_view() dict
+ }
+ class OrderStatus { <> pending·approved·filled·rejected·cancelled }
+ class OrderConflictError { <> }
+ class HITLConfigStore {
+ +level: int
+ +set_level(level, reason, op)
+ +snapshot() dict
+ }
+ class AutonomyLevel { <> +level +threshold_usdt }
+ class GuardrailSystem
+ class TradingLedger
+
+ OrderStore *-- Order
+ Order --> OrderStatus
+ OrderStore o-- TradingLedger
+ OrderStore o-- GuardrailSystem
+ OrderStore ..> OrderConflictError
+ HITLConfigStore ..> AutonomyLevel
+ HITLConfigStore ..> OrderStore : pending_count
+
+ class APIResponse~T~ {
+ <>
+ +data: T
+ +meta: Meta?
+ +links: Links?
+ }
+ class OrderCreate { <> pair·side·qty·price·stop_loss(obrig.) }
+ class OrderOut { <> +id +status +notional +rr }
+ class OrderDecisionPatch { <> decision: approve|reject }
+ class PortfolioMetricsOut { <> }
+ APIResponse ..> OrderOut
+ APIResponse ..> PortfolioMetricsOut
+ OrderCreate ..> Order : route → OrderStore.submit
+```
+
+**Máquina de estados do `Order` (HITL):**
+
+```mermaid
+stateDiagram-v2
+ [*] --> pending: submit()
+ pending --> filled: auto (notional≤threshold\n& not critical & risk_ok)
+ pending --> rejected: guardrail violation
+ pending --> approved: resolve(approve)\n[API/operador]
+ pending --> rejected: resolve(reject)
+ pending --> cancelled: wait_for_decision timeout (300s)
+ approved --> filled: mark_filled()\n[loop pós-execução]
+ filled --> [*]
+ rejected --> [*]
+ cancelled --> [*]
+```
+
+**Notas de design:**
+- **Contrato tipado ponta-a-ponta:** os DTOs Pydantic em `schemas.py` são embrulhados no envelope genérico `APIResponse[T]` (`data`/`meta`/`_links`, HATEOAS). O `openapi-typescript` gera `openapi.d.ts` a partir do OpenAPI; o CI roda `gen:types` + `git diff --exit-code` — **impossível o backend Python e o front JS divergirem** sem quebrar o build. Essa é a *entidade transversal* que a metodologia pedia.
+- **Validações de negócio no schema:** `OrderCreate` exige `stop_loss` (>0) e `reason` (≥10 chars); `OrderDecisionPatch` exige `operator_note` ao rejeitar. Regras de risco codificadas já na fronteira.
+- **Modelo dois-processos explícito:** a **API decide** (`resolve` grava `approved`/`rejected`), o **loop executa** (`wait_for_decision` vê `approved` → roda `ExecutionAgent` → `mark_filled`). `mark_filled` é atômico e idempotente (`WHERE status='approved'`) — sem duplo-preenchimento.
+- **Ponto de atenção (namespace):** `PATCH /v1/agents/{id}/config` (router `config`) coexiste com `GET /v1/agents/{id}/config` e `GET /v1/agents/{id}` (router `agents`) — mesma raiz `/v1/agents` servida por dois módulos. Além disso, há dois modelos de autonomia (`hitl.config` por valor US$ vs `hitl.progressive_autonomy` por trust-score) — só o primeiro está ligado às rotas.
+
+---
+
+### 5.8 Camada de Apresentação (Frontend React)
+
+**Objetivo:** estrutura de componentes do console e o único ponto de I/O com o backend.
+**Escopo:** `docs/design/pages/*`.
+
+```mermaid
+classDiagram
+ class App {
+ +screen : hashRoute
+ +SCREENS : registry
+ }
+ class Sidebar
+ class Header
+ class ErrorBoundary
+ class AlertDrawer
+ class ToastContainer
+ class ScreenOverview
+ class ScreenMarket
+ class ScreenOrders
+ class ScreenHITL
+ class ScreenAgents
+ class ScreenRisk
+ class ScreenBacktest
+ class ScreenJournal
+ class ScreenObservability
+ class ScreenSettings
+ class CT_API {
+ +getMetrics()
+ +getOrders()
+ +createOrder()
+ +decideOrder()
+ +subscribeAlerts() SSE
+ }
+ class UIKit {
+ +Card_Badge_KPI_Meter_PairSelect
+ }
+ class Charts {
+ +CandleChart_EquityChart_Gauge
+ }
+
+ App *-- Sidebar
+ App *-- Header
+ App *-- ErrorBoundary
+ App *-- AlertDrawer
+ App *-- ToastContainer
+ App --> ScreenOverview
+ App --> ScreenMarket
+ App --> ScreenOrders
+ App --> ScreenHITL
+ App --> ScreenAgents
+ App --> ScreenRisk
+ App --> ScreenBacktest
+ App --> ScreenJournal
+ App --> ScreenObservability
+ App --> ScreenSettings
+ ScreenOverview ..> CT_API
+ ScreenMarket ..> CT_API
+ ScreenOrders ..> CT_API
+ ScreenHITL ..> CT_API
+ ScreenOverview ..> Charts
+ ScreenOverview ..> UIKit
+ CT_API ..> openapi_d_ts : contrato
+```
+
+**Notas de design:**
+- **Arquitetura "classic scripts":** React/ReactDOM como UMD globals; cada arquivo compartilha o escopo global e publica símbolos em `window.*` (`window.Badge`, `window.CT_API`). Sem sistema de módulos em dev; o `build.mjs` (esbuild, `bundle:false`) transpila cada `.jsx` isoladamente em IIFE e auto-hospeda o React de produção. Simples, mas frágil quanto a ordem de carga e colisões globais — candidato natural a migrar para ES modules/bundler se o console crescer.
+- **`CT_API` é o único ponto de I/O:** `fetch` REST + `EventSource` (SSE) em `/v1/alerts`; desembrulha `j.data ?? j` (o envelope `APIResponse`); reescreve `BTC/USDT`→`BTC-USDT` nas rotas de mercado. Cada `Screen` possui suas chamadas — bom isolamento por tela.
+- **Roteamento por hash** (`#overview`, `#market`…) → SPA fallback no nginx (`try_files … /index.html`). E2E Playwright roda sobre mock (`USE_MOCK_DATA`), sem backend.
+
+---
+
+## 6. Diagramas de Sequência
+
+### 6.1 Ciclo de trading contínuo (fluxo de negócio principal)
+
+**Objetivo:** ordem temporal exata do pipeline `analyze_and_trade` dentro de um ciclo do loop.
+**Escopo:** loop → squad → agentes → guardrails → HITL → execução → ledger.
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Loop as OrchestratorLoop
+ participant Squad as SquadOrchestrator
+ participant CB as CircuitBreaker
+ participant SA as StrategyAgent
+ participant EX as ExchangeClient
+ participant RA as RiskAgent
+ participant GS as GuardrailSystem
+ participant HITL as approval_handler
(OrderStore)
+ participant EA as ExecutionAgent
+ participant L as TradingLedger
+
+ Loop->>L: log_process_event(agent_cycle_started)
+ Loop->>Squad: await analyze_and_trade(symbol)
+ Squad->>CB: is_open?
+ alt breaker aberto
+ CB-->>Squad: true
+ Squad-->>Loop: {success:false, "trading paused"}
+ else breaker fechado
+ Squad->>SA: await execute({symbol, timeframe})
+ SA->>EX: await fetch_ohlcv(symbol)
+ EX-->>SA: OHLCV (ou stub sintético)
+ SA->>SA: TA + regime + confidence (CoT LLM opcional)
+ SA-->>Squad: {signal, confidence, stub_used}
+ Squad->>Squad: _check_open_positions() (fecha SL/TP)
+ Squad->>L: log_signal()
+ alt confidence < 0.6
+ Squad-->>Loop: {success:false, "low confidence"}
+ else
+ Squad->>RA: await execute({signal, portfolio})
+ RA->>GS: validate_order(signal)
+ GS-->>RA: (approved, issues)
+ RA-->>Squad: {approved, validation}
+ Squad->>L: log_validation()
+ alt rejeitado
+ Squad->>L: log + emit_alert()
+ Squad-->>Loop: {success:false, "risk failed"}
+ else aprovado
+ Squad->>HITL: await approval_handler(signal)
+ Note over HITL: submete Order; auto-fill se ≤ threshold,
senão wait_for_decision (cross-process)
+ HITL-->>Squad: order_id | None
+ Squad->>L: log_hitl_approval()
+ alt humano aprovou
+ Squad->>EA: await execute({signal, quantity})
+ EA->>EX: await create_order() (paper)
+ EX-->>EA: fill (preço+fee)
+ EA-->>Squad: {success, order_id, executed_price}
+ Squad->>L: log_execution() + log_fill()
+ Squad->>HITL: fill_callback(order_id) → mark_filled
+ else rejeitado
+ Squad-->>Loop: {success:false}
+ end
+ end
+ end
+ end
+ Loop->>L: log_process_event(agent_cycle_completed) + heartbeat
+```
+
+**Notas:** todos os pontos `await` estão em `run_cycle`/`analyze_and_trade`/agentes; falha de um símbolo é *fail-soft* (o loop emite `agent_cycle_failed` e segue). A ordem `Strategy → posições → Risk → HITL → Execution` é invariante.
+
+### 6.2 Aprovação HITL cross-process (API decide, loop executa)
+
+**Objetivo:** mostrar a coordenação dos dois processos via SQLite compartilhado.
+
+```mermaid
+sequenceDiagram
+ autonumber
+ actor Op as Operador (Console/Dashboard)
+ participant API as API FastAPI
+ participant DB as SQLite (orders)
+ participant Store as OrderStore
+ participant Loop as OrchestratorLoop
+
+ Loop->>Store: submit(Order) [notional > threshold]
+ Store->>DB: INSERT status='pending'
+ Store->>Loop: wait_for_decision(id) inicia polling
+ loop a cada poll_interval (2s)
+ Loop->>DB: SELECT status WHERE id=?
+ DB-->>Loop: 'pending'
+ end
+ Op->>API: PATCH /v1/orders/{id}/status {approve}
+ API->>Store: resolve(id, approved=true)
+ Store->>DB: UPDATE status='approved'
+ Note over Loop,DB: próximo poll vê 'approved'
+ Loop->>DB: SELECT status
+ DB-->>Loop: 'approved'
+ Store-->>Loop: True
+ Loop->>Loop: ExecutionAgent.execute() (paper fill)
+ Loop->>Store: mark_filled(id)
+ Store->>DB: UPDATE status='filled' WHERE status='approved'
+ Note over Op,DB: timeout de 300s sem decisão → cancel() (fail-closed)
+```
+
+**Notas de design:** não há mensageria — o acoplamento é o **estado no SQLite (WAL)**. Latência de aprovação ≤ `poll_interval`. Idempotência garantida pelo `WHERE status='approved'`.
+
+### 6.3 Backtest assíncrono (job)
+
+```mermaid
+sequenceDiagram
+ autonumber
+ actor Op
+ participant API as /v1/backtest
+ participant DB as SQLite (backtest_jobs)
+ participant Task as asyncio Task
+ participant Eng as BacktestEngine/MC/WF
+
+ Op->>API: POST /v1/backtest/run (config)
+ API->>DB: INSERT job status='running'
+ API->>Task: asyncio.create_task(_run_job)
+ API-->>Op: 202 {job_id, running}
+ Task->>Eng: run(strategy, ohlcv)
+ Eng-->>Task: BacktestResult
+ Task->>DB: UPDATE status='done', result=...
+ Op->>API: GET /v1/backtest/jobs/{id} (polling)
+ API->>DB: SELECT
+ DB-->>API: status/result
+ API-->>Op: APIResponse[BacktestJobOut]
+ Note over API,DB: no startup, _reconcile_orphans marca
jobs 'running' interrompidos como errored
+```
+
+---
+
+## 7. Diagramas de Atividades
+
+### 7.1 Geração de sinal + blend de confiança (StrategyAgent)
+
+**Objetivo:** o workflow com múltiplos caminhos de decisão dentro de `StrategyAgent.execute`.
+
+```mermaid
+flowchart TD
+ A([execute task]) --> B{exchange_client?}
+ B -->|não| S[stub_analysis sintético]
+ B -->|sim| F[fetch_ohlcv]
+ F --> G{candles ≥ MIN_CANDLES?}
+ G -->|não| S
+ G -->|sim| TA[TA: indicators, S/R, volume, regime, divergência]
+ S --> R[detect_regime → eligible_strategies]
+ TA --> R
+ R --> SEL{estratégia elegível?}
+ SEL -->|não| HOLD[signal = HOLD]
+ SEL -->|sim| STRAT[STRATEGY_REGISTRY get → strategy.analyze]
+ STRAT --> CONF[confidence determinística
trend+confluência+S/R+volume+divergência]
+ CONF --> BLEND[blend 60% estratégia + 40% agente]
+ BLEND --> LLM{LLM ativo & ação≠HOLD?}
+ LLM -->|sim| COT[reason_json → blend 50/50 + tese]
+ LLM -->|não| SKIP[mantém score]
+ COT --> LOG
+ SKIP --> LOG
+ HOLD --> LOG[log_decision]
+ LOG --> OUT([retorna signal + confidence + stub_used])
+```
+
+### 7.2 Decisão de execução com circuit breaker e proteções (visão de negócio)
+
+```mermaid
+flowchart TD
+ Start([cycle]) --> CB{circuit_breaker.is_open?}
+ CB -->|sim| Pause[[pausa 24h — skip]]
+ CB -->|não| Sig[gera sinal]
+ Sig --> Conf{confidence ≥ 0.6?}
+ Conf -->|não| Skip1[skip]
+ Conf -->|sim| Risk{guardrails ok?}
+ Risk -->|não| Alert[emit alert + reject]
+ Risk -->|sim| Thr{notional ≤ threshold
& not critical?}
+ Thr -->|sim| Auto[auto-approve → fill]
+ Thr -->|não| Wait{humano aprova
em 300s?}
+ Wait -->|não| Cancel[cancel/reject]
+ Wait -->|sim| Exec[ExecutionAgent paper fill]
+ Auto --> Ledger[log fill]
+ Exec --> Ledger
+ Ledger --> Close{preço atinge SL/TP
em ciclo futuro?}
+ Close -->|sim| PnL[registra pnl → circuit_breaker.record_trade_result]
+ PnL --> Trip{perda diária ≥4%
ou 3 losses seguidas?}
+ Trip -->|sim| Pause
+ Trip -->|não| Start
+```
+
+**Notas de design:** há **paralelismo real** apenas no `UnifiedOrchestrator` (`ParallelResourceManager` com `asyncio.Semaphore` + `gather`) e na rota `/v1/market/{pair}/confluence` (`asyncio.gather` sobre timeframes 1h/4h/1d). O pipeline de trading em si é **sequencial e determinístico** por ciclo — decisão consciente para auditabilidade.
+
+---
+
+## 8. Diagrama de Implantação
+
+**Objetivo:** nós físicos/containers, redes e protocolos.
+**Escopo:** `docker-compose.prod.yml` (topologia self-contained) + `deploy/`.
+
+```plantuml
+@startuml
+node "Browser do Operador" as browser {
+ artifact "React Console (dist estático)" as console
+}
+
+node "Host de Produção (Docker)" {
+ node "nginx container\n(deploy/console.Dockerfile)" as nginx {
+ artifact "console dist (baked)"
+ portin "443/tcp (HTTPS)"
+ portin "80/tcp (ACME + 301)"
+ }
+ node "app container\n(uvicorn/FastAPI)" as app {
+ component "API /v1 + SSE"
+ portin "8000 (interno)"
+ }
+ node "orchestrator container\n(main_loop)" as loop {
+ component "OrchestratorLoop"
+ }
+ node "prometheus container" as prom
+ node "certbot sidecar" as certbot
+ database "Volume ./data" as data {
+ artifact "SQLite WAL (orders, cycle_events, ledger_events)"
+ artifact "JSONL (ledger, alerts, XES)"
+ }
+}
+
+cloud "Exchange (CCXT)" as ex
+cloud "LLM Provider" as llm
+
+browser --> nginx : HTTPS (REST + SSE)\nAPIResponse envelope, X-API-Key
+nginx --> app : HTTP proxy /v1 /health\n(--forwarded-allow-ips 172.28.0.2)
+app --> data : read/write
+loop --> data : read/write (mesmo volume)
+loop ..> ex : dry-run/paper (offline por padrão)
+app ..> ex : market data
+loop ..> llm : CoT/reflection (opcional)
+prom --> app : scrape /metrics (HTTP)
+certbot ..> nginx : renova certs (HTTP-01)
+
+note bottom of loop
+ Sem porta publicada.
+ Compartilha lifecycle=NENHUM com app:
+ restart da API não para o trading.
+end note
+@enduml
+```
+
+**Notas de design:**
+- **Três topologias** no repo: `docker-compose.yml` (dev, todas as portas expostas + Prometheus/Grafana, perfis `scale` com Postgres/Redis), `docker-compose.prod.yml` (só nginx publica 80/443; app/loop/prometheus internos em rede `edge` 172.28.0.0/24) e `docker-compose.vps.yml` (sem portas de host, atrás de gateway `btv-nginx-prod` compartilhado).
+- **Isolamento de rede em prod:** `app` confia apenas no nginx para `X-Forwarded-*` (rate-limit por IP real). `APP_ENV=production` força `API_KEYS` + `CORS_ORIGINS` explícito (fail-closed no boot).
+- **Escala horizontal (ADR-005):** trocar SQLite→Postgres e in-memory rate limiter→Redis é opt-in via env, sem mudança de código (graças à abstração `db.connection` e `build_rate_limiter`). **Ressalva:** o loop é *single-instance* — o modelo de HITL por polling de SQLite não é trivialmente replicável para N loops.
+
+---
+
+## 9. Análise Crítica: Coesão, Acoplamento e Refatoramento
+
+### 9.1 Pontos fortes
+- **Fluxo de trading coeso e auditável:** o pipeline `Strategy→Risk→HITL→Execution` é linear, testado e totalmente registrado no ledger/XES. Fail-safe em todas as fronteiras (dry-run, LLM opcional, fail-closed no HITL, circuit breaker).
+- **Separação de processos correta:** API e loop desacoplados por lifecycle, comunicando via estado compartilhado — resiliente a restart.
+- **Contrato tipado ponta-a-ponta:** `openapi.d.ts` + drift-gate no CI eliminam divergência backend↔frontend. Exemplar.
+- **Infra portável:** abstração de DB (SQLite↔Postgres) e rate limiter (memória↔Redis) por configuração.
+- **Padrões bem aplicados:** Strategy (estratégias), Repository (ledger/order/journal/alert stores), Event Sourcing leve (ledger→métricas derivadas), Registry/Factory (`STRATEGY_REGISTRY`, `MCPToolRegistry`), Observer/PubSub (`AlertBus`→SSE), Template Method (`BaseAgent`).
+
+### 9.2 Pontos de atenção (dívida técnica / refatoramento)
+
+| # | Achado | Impacto | Recomendação |
+|---|---|---|---|
+| 1 | **Duas fundações de agente** (`BaseAgent` async vs `SafeAgentBase` sync) sem ponte | Confusão conceitual; `SafeAgentBase` não usado no trading | Decidir uma base única ou documentar claramente os dois propósitos |
+| 2 | **Cluster "BuildToValue" paralelo** (`UnifiedOrchestrator` + planning/routing/consensus/chains/parallel + agentes de engenharia) não exercitado pelo trading | Código potencialmente morto; ~⅓ dos módulos de orquestração | Isolar em pacote opcional ou remover; medir cobertura real |
+| 3 | **Colisões de nome:** `SquadOrchestrator`×2, `AdaptivePlanner`×2, `ContinuousEvaluator`×2, `MemoryStore`×2, `Guardrail`×2 | Erros de import, ambiguidade em revisões | Renomear para nomes qualificados por propósito |
+| 4 | **Duas políticas de validação de ordem** (`GuardrailSystem` vs `SecurityConfig.validate_order`) e **dois modelos de autonomia** (US$ threshold vs trust-score) | Regra de negócio duplicada; risco de divergência | Eleger fonte única de política de risco/autonomia |
+| 5 | **Sizing por `position_size_pct` fixo** enquanto `KellyCriterion`/`PositionSizer`/`CapitalProtections` existem prontos | Subaproveitamento de gestão de risco | Plugar Kelly/proteções no `SquadOrchestrator._position_quantity` |
+| 6 | **Namespace `/v1/agents/...` servido por dois routers** (`agents` e `config`) | Manutenção confusa; risco de conflito de rota | Consolidar num único router |
+| 7 | **HITL por polling de SQLite** acopla loop e API ao arquivo | Latência de até `poll_interval`; loop single-instance | Considerar `LISTEN/NOTIFY` (Postgres) ou fila se escalar |
+| 8 | **Frontend "classic scripts" com globals `window.*`** | Frágil a ordem de carga/colisões conforme cresce | Migrar para ES modules + bundler quando justificar |
+
+### 9.3 Métricas qualitativas de acoplamento
+- **Núcleo estável correto:** `core` é dependência de quase tudo, mas depende de pouco (config/db) — *stable dependencies principle* respeitado.
+- **Acoplamento aferente alto e saudável** em `TradingLedger` e `db.connection` (são seams intencionais).
+- **Acoplamento eferente problemático** no `UnifiedOrchestrator` (compõe 9 subsistemas) — sintoma de God Object no cluster genérico, reforçando o achado #2.
+- **Desacoplamento por callback** (`GuardrailSystem.alert_sink`, `state_db_provider`, `fill_callback`, `approval_handler`) é usado consistentemente — bom uso de inversão de dependência para evitar imports cíclicos.
+
+---
+
+## Apêndice — Rastreabilidade (entidade → arquivo)
+
+| Entidade | Arquivo-fonte |
+|---|---|
+| `BaseAgent` | `src/agents/base_agent.py` |
+| `StrategyAgent` / `RiskAgent` / `ExecutionAgent` | `src/agents/{strategy,risk,execution}_agent.py` |
+| `AgentRegistry` / `AGENT_REGISTRY` | `src/agents/registry.py` |
+| `SquadOrchestrator` / `CircuitBreaker` | `src/orchestration/squad_orchestrator.py` |
+| `OrchestratorLoop` | `src/orchestration/orchestrator_loop.py` |
+| `UnifiedOrchestrator` | `src/orchestration/unified_orchestrator.py` |
+| `OrderStore` / `Order` / `OrderStatus` | `src/hitl/orders.py` |
+| `HITLConfigStore` / `AutonomyLevel` | `src/hitl/config.py` |
+| `GuardrailSystem` | `src/safety/guardrails.py` |
+| `SecurityConfig` | `src/safety/security_config.py` |
+| `SafeAgentBase` + guardrails | `src/core/safe_agent_base.py` |
+| `TradingLedger` | `src/core/ledger.py` |
+| `PortfolioMetricsCalculator` / `PortfolioMetrics` | `src/core/metrics.py` |
+| `ExchangeClient` | `src/core/exchange_client.py` |
+| `Alert` / `AlertStore` / `AlertBus` | `src/core/alerts.py` |
+| `db.connection` / backends | `src/core/db.py` |
+| `Settings` | `src/core/config.py` |
+| `BaseStrategy` / `STRATEGY_REGISTRY` | `src/strategies/` |
+| `TechnicalAnalyzer` / `TechnicalIndicators` | `src/analysis/indicators.py` |
+| `BacktestEngine` / `WalkForwardValidator` / `MonteCarloSimulator` | `src/backtest/` |
+| `KellyCriterion` / `PositionSizer` / `CapitalProtections` | `src/risk/` |
+| API gateway + middleware | `src/api/main.py` |
+| DTOs / contrato Pydantic | `src/api/schemas.py` |
+| Rotas `/v1/*` | `src/api/routes/*.py` |
+| Providers de dependência | `src/api/deps.py` |
+| Dashboard | `src/dashboard/app.py` |
+| Console React + `CT_API` | `docs/design/pages/*.jsx`, `apiClient.js` |
+| **Contrato TS gerado** | `docs/design/pages/openapi.d.ts` |
+| Topologias de deploy | `docker-compose*.yml`, `deploy/`, `Dockerfile` |
+
+---
+
+> **Documento gerado por análise estática recursiva de todo o repositório.** Diagramas em **Mermaid** (renderizam nativamente no GitHub) e **PlantUML** (casos de uso e implantação — requerem renderizador PlantUML). Nomes de classes/módulos preservados *case-sensitive* conforme o código-fonte.