chore: avance acumulado de sesiones previas (reorg dev/issues + ajustes)

Reorganizacion de dev/issues en subcarpetas (completed/, cpp/, gamedev/,
kanban/, trading/, imagegen/, matrix/) y cambios acumulados en cmd/fn/pyrunner,
.claude/commands y settings. Trabajo de otro LLM/sesion, commiteado a peticion
del usuario para desbloquear el working tree. Excluido logs/ardour_mcp_server.log (ruido).
This commit is contained in:
2026-06-30 14:43:51 +02:00
parent 5501507588
commit a3f75d61ec
125 changed files with 421 additions and 203 deletions
@@ -0,0 +1,81 @@
---
id: "0088"
title: "Trading & Skill Management Roadmap"
status: pendiente
type: epic
domain:
- trading
scope: cross-stack
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088 — Trading & Skill Management Roadmap
**Status:** pendiente
**Created:** 2026-05-14
**Type:** planning
**Related:** 0085 (telemetry), 0086 (delegation), 0087 (capability discovery), 0068 (e2e validation), 0069 (autonomous loop)
**Sub-issues:** 0088a0088j
## Problema
Trading manual (multi-fuente, multi-cuenta) es una habilidad medible que se beneficia del mismo bucle reactivo que el registry: CONSTRUIR → EJECUTAR → RECOPILAR → ANALIZAR → MEJORAR. Falta la instanciacion concreta: ledger inmutable, broker connectors, strategy contract, risk gates, reflection log y un live runner gobernado por assertions. El registro ya tiene primitivas puras de finance (SMA/EMA/RSI/Bollinger/VWAP/Sharpe/Drawdown/GBM/Avellaneda-Stoikov/Hawkes) e impuras minimas (`fetch_ohlcv`, `stream_ticks`, `tick_to_ohlcv`, `write_ohlcv_to_parquet`, `load_ohlcv_from_duckdb`), pero no hay broker integration, ledger ni runner.
Objetivo: tratar **trading como skill autoiterado** sobre el bucle reactivo, con metricas explicitas (PnL realizado, drawdown, sharpe live vs backtest, slippage, win-rate, adherencia a reflection log) que se evaluan por assertions y generan proposals automaticas.
## Filosofia
1. **Skill = control loop con ledger inmutable**. Sin INSERT-only ledger no hay verdad; sin verdad no hay mejora.
2. **Paper-first**. Adapter `broker_paper` cumple la misma interfaz que cualquier broker real. Switch por config.
3. **Reflection obligatoria**. La UI del journal fuerza post-mortem antes de cerrar un trade. Sin reflexion, el bucle no progresa.
4. **Kill-switch como funcion del registry**, invocable desde CLI/UI/cron. Definida ANTES del primer broker live.
5. **Bucle reactivo del registry == bucle reactivo del skill**. Las assertions que vigilan al runner son del mismo tipo que las que vigilan a las apps.
## Pilares + sub-issues
| # | Sub-issue | Pieza | Tipo |
|---|---|---|---|
| 0088a | Project scaffolding `projects/trading/` + vault `market_data` | infra |
| 0088b | Capability group `market_data` + adapters (binance, yfinance, alphavantage; cpp/py) | feature |
| 0088c | Capability group `broker` + interface comun + adapter `broker_paper` | feature |
| 0088d | App `portfolio_tracker` (ledger INSERT-only + sqlite_api + Mantine UI) | feature |
| 0088e | Capability group `strategy` + contrato Strategy + 2 pure strategies de referencia | feature |
| 0088f | Capability group `risk` (kelly, max_loss, exposure_cap, correlation_filter) + kill_switch | feature |
| 0088g | App `backtester` (corre Strategy contra historico, produce reporte deterministico) | feature |
| 0088h | App `live_runner` (service, paper-only on master; broker real detras de feature flag OFF) | feature |
| 0088i | App `trading_journal` (reflection log + UI con tags + screenshots) | feature |
| 0088j | Wiring del bucle reactivo: `e2e_checks` + assertions especificas de trading + proposals automaticas | infra |
## Decisiones criticas (resolver antes de 0088c)
- **Multi-account, multi-currency, multi-asset desde dia 1** en el schema del ledger. Migracion posterior es dolorosa.
- **`client_order_id` deterministico** para idempotencia. Hash de `(strategy_id, intent_ts, intent_hash)`.
- **Reconciliacion** broker ↔ ledger en cada ciclo del live runner. Discrepancia > epsilon → halt + proposal.
- **Backtest != live**. Assertion sobre `|sharpe_backtest - sharpe_live|` y sobre `slippage_realized - slippage_modeled`.
- **Datos sensibles**. Credenciales de broker en `~/.fn_secrets` o gestor externo, nunca en `operations.db`.
## Plan de ejecucion
Tres tandas, cada una mergeable a master con master desplegable:
- **Tanda A (foundations)**: 0088a + 0088d (ledger sin broker) + 0088f (risk + kill_switch como funciones puras).
- **Tanda B (paper loop cerrado)**: 0088b + 0088c (solo paper) + 0088e + 0088g.
- **Tanda C (live + bucle reactivo)**: 0088h (behind flag OFF) + 0088i + 0088j. Activacion live = commit explicito que flip-flop el flag tras N sesiones paper estables.
## Aceptacion del roadmap
- Cada sub-issue cerrado con su `e2e_checks` declarado en `app.md` cuando aplique.
- `docs/capabilities/trading.md` (o split: market_data / broker / strategy / risk / journal) listando primitivas reutilizables.
- Kill-switch invocable: `./fn run halt_all_strategies_py_finance` (o equivalente). Verificable en frio.
- Primer ciclo paper de 7 dias con ledger reconciliado, reflection log >= 1 entry por trade, assertions pasadas o explicadas via proposals.
## No-objetivos
- Optimizacion HFT, latencia sub-ms, market making real en exchange.
- Multi-tenant (esto es para uso personal).
- Estrategias proprietarias secretas — el registry es publico-local, cualquier estrategia debe encajar como funcion pura testeable. Edge se protege via parametros en `~/.fn_secrets/strategies.yaml`, no via codigo oculto.
@@ -0,0 +1,47 @@
---
id: "0088a"
title: "Trading: project scaffolding"
status: pendiente
type: feature
domain:
- trading
scope: multi-app
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: [ausente-ready]
---
# 0088a — Trading: project scaffolding
**Status:** pendiente
**Created:** 2026-05-14
**Type:** infra
**Parent:** 0088
**Blocks:** 0088b, 0088c, 0088d
## Problema
No existe `projects/trading/`. Sin la carpeta + `project.md` + vault, no hay donde colgar las apps y analyses subsiguientes ni manera de que `fn index` les asigne `project_id`.
## Piezas
1. `projects/trading/project.md` con frontmatter completo (name, description, tags, repo_url).
2. `projects/trading/apps/` (vacio inicialmente, alli iran portfolio_tracker, backtester, live_runner, trading_journal).
3. `projects/trading/analysis/` (vacio; lugar para `strategy_lab` futuro).
4. `projects/trading/vaults/vault.yaml` declarando vault `market_data`.
5. Vault real en `~/vaults/market_data/{raw,processed,exports}` + symlink en `projects/trading/vaults/market_data`.
6. `fn index` para registrar el proyecto y el vault.
7. Entrada en `dev/issues/README.md` tras crear el roadmap.
## Aceptacion
- `mcp__registry__fn_show id="trading"` devuelve el proyecto con su descripcion.
- `mcp__registry__fn_doctor subcommand="sync"` no reporta drift para el vault.
- `fn doctor artefacts` no marca `git_not_initialized` para el proyecto (queda dentro de fn_registry, no es sub-repo propio).
## No-objetivos
- Crear apps todavia. Solo scaffolding.
@@ -0,0 +1,53 @@
---
id: "0088b"
title: "Trading: capability group `market_data`"
status: pendiente
type: feature
domain:
- trading
- meta
scope: multi-app
priority: alta
depends: ["0088a"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088b — Trading: capability group `market_data`
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088a
**Blocks:** 0088g, 0088h
## Problema
El registry ya tiene `fetch_ohlcv_go_finance`, `stream_ticks_go_finance`, `tick_to_ohlcv_go_finance`, `load_ohlcv_from_duckdb_go_finance`, `write_ohlcv_to_parquet_go_finance` — pero no estan unificadas detras de un contrato comun ni cubren multi-fuente. Hace falta un capability group `market_data` que agrupe adapters por fuente (binance, alphavantage, yfinance, csv local) bajo la misma firma.
## Piezas
1. Tag plano `market_data` aplicado a las funciones existentes que ya cubren OHLCV/ticks.
2. Nuevas funciones impuras (una por fuente, todas siguen la misma forma de salida `OHLCVFrame`):
- `fetch_ohlcv_binance_py_finance` (REST).
- `fetch_ohlcv_yfinance_py_finance` (yfinance lib).
- `fetch_ohlcv_alphavantage_py_finance` (API key via env).
- `fetch_ohlcv_csv_py_finance` (local file, util para datasets snapshot).
3. Funcion pura `normalize_ohlcv_frames_py_finance` que recibe frames de distintas fuentes y devuelve uno reconciliado (mismo timezone UTC, mismas columnas, ordenado por timestamp).
4. Pipeline impuro `ingest_ohlcv_multisource_py_finance` que orquesta fetch_* + normalize + write a parquet en `~/vaults/market_data/raw/<source>/<symbol>/<interval>.parquet`.
5. Pagina madre `docs/capabilities/market_data.md` con tabla + ejemplo canonico end-to-end.
## Aceptacion
- `mcp__registry__fn_search query="" tag="market_data"` devuelve >= 5 funciones.
- `docs/capabilities/market_data.md` enlazado desde `docs/capabilities/INDEX.md`.
- Pipeline `ingest_ohlcv_multisource` documentado con `## Ejemplo` lanzable (issue 0087).
- Funciones cumplen reglas `purity.md` + `ids_naming.md`.
## No-objetivos
- Streaming en vivo de multiples fuentes (eso queda para el live_runner, 0088h).
- Indicadores tecnicos (ya estan en finance/).
@@ -0,0 +1,58 @@
---
id: "0088c"
title: "Trading: broker interface + adapter paper"
status: pendiente
type: feature
domain:
- trading
scope: multi-app
priority: alta
depends: ["0088a", "0088d"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088c — Trading: broker interface + adapter paper
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088a, 0088d (necesita ledger funcionando para reflejar fills)
**Blocks:** 0088g, 0088h, 0088l (broker real)
## Problema
No hay contrato comun broker. Sin contrato, cada broker se cablearia ad-hoc en cada app. El primer adapter debe ser `paper` — simula fills usando OHLCV + modelo de slippage simple — y debe ser totalmente intercambiable con un broker real bajo la misma interfaz.
## Piezas
1. Tipo Python `BrokerInterface` (protocolo / abstract class) con metodos minimos:
- `place_order(order: OrderIntent) -> OrderAck`
- `cancel_order(client_order_id: str) -> None`
- `get_positions() -> list[Position]`
- `get_balance() -> Balance`
- `stream_fills() -> Iterator[Fill]`
2. Tipos del registry: `OrderIntent`, `OrderAck`, `Fill`, `Position`, `Balance` en `python/types/finance/`.
3. Funciones puras de soporte:
- `make_client_order_id_py_finance(strategy_id, intent_ts, intent_payload) -> str` (idempotencia).
- `simulate_fill_paper_py_finance(intent, ohlcv_snapshot, slippage_model) -> Fill`.
4. Adapter `broker_paper`:
- `broker_paper_place_order_py_finance` (impure: persiste intent + simula fill via OHLCV ultimo).
- `broker_paper_get_balance_py_finance` (impure: lee ledger).
- `broker_paper_get_positions_py_finance` (impure: lee ledger).
- `broker_paper_stream_fills_py_finance` (impure: tail del ledger).
5. Tag `broker` aplicado a interface + adapter functions. Pagina madre `docs/capabilities/broker.md`.
## Aceptacion
- Demo en `analysis/strategy_lab` (o test): `place_order → fill → reconciliacion ledger → balance updated`.
- `BrokerInterface` implementable por un broker real sin tocar el codigo de la app que lo consume.
- `client_order_id` deterministico: misma intent reproduce misma id (idempotencia comprobada).
## No-objetivos
- Adapters binance/IB/alpaca reales — sale a 0088l.
- Order book matching realista (FIFO/queue position). El paper inicial usa slippage simple sobre OHLCV.
@@ -0,0 +1,65 @@
---
id: "0088d"
title: "Trading: app `portfolio_tracker` (ledger INSERT-only)"
status: pendiente
type: feature
domain:
- trading
scope: app-scoped
priority: alta
depends: ["0088a"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088d — Trading: app `portfolio_tracker` (ledger INSERT-only)
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088a
**Blocks:** 0088c, 0088h
## Problema
Necesitamos un ledger inmutable multi-account, multi-currency, multi-asset desde el dia 1 + UI minima para inspeccionarlo. Es la fuente de verdad del estado de cuentas; cualquier broker (paper o real) escribe contra este ledger.
## Piezas
1. App Go en `projects/trading/apps/portfolio_tracker/` (service + sqlite_api + Mantine UI).
2. Schema (en `migrations/*.sql`, aditivo):
- `accounts` (id, label, currency_base, broker_kind, created_at).
- `instruments` (id, symbol, kind, currency_quote).
- `transactions` (id, account_id, instrument_id, ts, side, qty, price, fee, fee_currency, client_order_id, broker_fill_id, raw_json, source). **INSERT-only**, indice por `(account_id, ts)` y unique sobre `client_order_id`.
- `equity_snapshots` (id, account_id, ts, equity_quote_currency, unrealized_pnl, realized_pnl_delta).
- `valuation_marks` (id, instrument_id, ts, price, source). Marca de precio para valoracion.
3. Funciones puras del registry (no inline en la app):
- `reduce_positions_from_transactions_py_finance(txs) -> list[Position]`.
- `compute_realized_pnl_py_finance(txs) -> float`.
- `compute_unrealized_pnl_py_finance(positions, marks) -> float`.
- `compute_equity_py_finance(balance, positions, marks) -> float`.
4. API HTTP via sqlite_api/CRUD generator sobre las 5 tablas. Endpoint custom `/api/equity_curve?account_id=X`.
5. UI Mantine con @fn_library:
- Lista de cuentas con balance + equity actual.
- Detalle de cuenta: tabla de transactions, grafico equity curve, posiciones abiertas.
- Form de transaction manual (para depositos/retiradas/ajustes que no vienen de broker).
6. `e2e_checks` en `app.md`:
- `build_frontend`, `build_backend`, `smoke` (arranca puerto efimero + GET `/api/health`).
- `ledger_immutable`: intenta UPDATE/DELETE sobre `transactions` y espera fallo (constraint trigger).
- `reconciliation`: tras insertar N transactions sinteticas, `reduce_positions` retorna lo esperado.
7. Tag `service` en `app.md`. Tag de grupo `journal`/`trading`.
## Aceptacion
- Ledger no permite UPDATE/DELETE de transactions (trigger SQLite).
- Reconciliacion determinista: dadas las mismas transactions, mismo resultado de posiciones/balance/equity.
- `fn doctor artefacts` y `fn doctor cpp-apps` (n/a aqui) sin errores.
- App lanzable como service con `deploy_server` (no obligatorio para cerrar el issue, pero `app.md` debe declarar deploy target).
## No-objetivos
- Integracion broker. Eso entra en 0088c.
- Computo de impuestos/FIFO fiscal. Solo PnL operativo.
@@ -0,0 +1,58 @@
---
id: "0088e"
title: "Trading: capability group `strategy` + Strategy contract"
status: pendiente
type: feature
domain:
- trading
- meta
scope: multi-app
priority: alta
depends: ["0088a"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088e — Trading: capability group `strategy` + Strategy contract
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088a
**Blocks:** 0088g, 0088h
## Problema
No hay contrato de estrategia. Sin contrato cada estrategia se cablea distinto y backtester/live_runner no pueden tratarlas uniformemente. El contrato debe ser **puro**: entrada = market state + portfolio state + params; salida = lista de `OrderIntent` (no efectos).
## Piezas
1. Tipos del registry en `python/types/finance/`:
- `MarketSnapshot` (OHLCV ventana + last_price + ts).
- `PortfolioSnapshot` (balance + positions).
- `StrategyParams` (dict tipado por estrategia, validado via JSON Schema).
- `OrderIntent` (lado, qty, instrument, kind=market/limit, price_limit, ttl, reason).
2. Contrato Strategy (Python protocol):
```
def decide(market: MarketSnapshot, portfolio: PortfolioSnapshot, params: StrategyParams) -> list[OrderIntent]
```
Funcion **pura**. Sin I/O, sin random sin semilla, sin tiempo system.
3. Dos estrategias de referencia (puras):
- `strategy_sma_cross_py_finance` (SMA rapida/lenta cross → entry/exit).
- `strategy_rsi_meanrev_py_finance` (RSI < 30 → long, > 70 → short con caps).
4. Funcion `validate_strategy_output_py_finance(intents, portfolio, params)` que aplica sanity (qty>0, instrumento valido, no oversize) antes de pasar a risk.
5. Tag `strategy`. Pagina madre `docs/capabilities/strategy.md` con ejemplo canonico end-to-end (snapshot fake → decide → list[intent]).
6. Tests deterministicos: dado snapshot fijado, `decide` retorna lo esperado.
## Aceptacion
- `mcp__registry__fn_search query="" tag="strategy"` devuelve interface + >= 2 estrategias.
- Tests pasan en `go test`/`pytest`.
- Estrategias son puras: `purity: pure` en frontmatter; sin imports impuros.
## No-objetivos
- Estrategias optimizadas, backtest report (sale a 0088g), parametrizacion via UI (analysis/strategy_lab futuro).
@@ -0,0 +1,58 @@
---
id: "0088f"
title: "Trading: capability group `risk` + kill_switch"
status: pendiente
type: feature
domain:
- trading
- meta
scope: multi-app
priority: alta
depends: ["0088a"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088f — Trading: capability group `risk` + kill_switch
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088a
**Blocks:** 0088h (live_runner no arranca sin esto)
## Problema
Cualquier intent emitido por una Strategy debe pasar por un risk gate **antes** de tocar el broker. El kill_switch debe ser invocable desde CLI/cron/UI antes de que exista un broker live — fail-safe absoluto.
## Piezas
1. Funciones puras:
- `position_size_kelly_py_finance(edge, variance, bankroll, kelly_fraction)`.
- `position_size_fixed_risk_py_finance(stop_distance, risk_per_trade_pct, equity)`.
- `cap_max_loss_per_trade_py_finance(intent, stop_price, equity, max_loss_pct)`.
- `cap_exposure_py_finance(intent, current_exposure, exposure_limit)`.
- `filter_correlation_py_finance(intents, current_positions, correlation_matrix, max_corr)`.
- `apply_risk_pipeline_py_finance(intents, portfolio, risk_config) -> list[OrderIntent]` (pipeline puro que compone las anteriores).
2. Funcion impura `halt_all_strategies_py_finance(reason)`:
- Escribe flag `halted=true` en `~/.fn_state/trading_halt.json` (o tabla `system_state` en ledger).
- Notifica (telegram via 0061 si aplica).
- Idempotente: re-llamar no falla.
3. Funcion pura `is_halted_py_finance(state_path) -> bool` que el live_runner consulta cada ciclo.
4. Funcion `release_halt_py_finance(reason)` (impura, escribe). Solo via CLI manual, NO via API.
5. Tag `risk`. Pagina madre `docs/capabilities/risk.md`.
6. Tests deterministicos sobre cada cap/filter.
## Aceptacion
- `./fn run halt_all_strategies` deja el sistema en estado halted en frio (verificable con `is_halted`).
- Cualquier intent que falle un cap retorna `[]` (no se emite), motivo logueado en operations.db del live_runner.
- Kill_switch invocable sin que el live_runner este corriendo (state file persistente).
## No-objetivos
- Risk dinamico aprendido (eso es feature futura). Risk_config se lee de YAML.
- UI para risk_config: editable a mano en YAML primero.
@@ -0,0 +1,56 @@
---
id: "0088g"
title: "Trading: app `backtester`"
status: pendiente
type: feature
domain:
- trading
scope: app-scoped
priority: alta
depends: ["0088b", "0088c", "0088d", "0088e", "0088f"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088g — Trading: app `backtester`
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088b, 0088c, 0088d, 0088e, 0088f
**Blocks:** 0088h (activar live solo tras backtest verde + paper estable)
## Problema
Necesitamos un backtest harness deterministico que corra una Strategy contra OHLCV historico, aplique el mismo pipeline de risk que en live, simule fills via `broker_paper`, y produzca un reporte estandarizado. Sin este harness, no hay forma de comparar estrategias ni de validar que `sharpe_backtest ≈ sharpe_live`.
## Piezas
1. App `projects/trading/apps/backtester/` (CLI + opcional web).
2. Pipeline canonico: `load_ohlcv → loop ticks → Strategy.decide → apply_risk → broker_paper → ledger → siguiente`.
3. Reporte deterministico (JSON + markdown) con:
- Total return, CAGR, sharpe, sortino, max drawdown, calmar, win-rate, avg win/loss, exposure %.
- Equity curve (lista timestamp, equity).
- Tabla de trades con `client_order_id`.
4. CLI:
- `./fn run backtester --strategy <id> --params params.yaml --symbol BTCUSDT --interval 1h --from 2024-01-01 --to 2024-12-31`.
5. Determinismo: misma semilla, mismos datos, mismo reporte byte-a-byte.
6. `e2e_checks`:
- `build`, `smoke` (corre backtest dummy 100 velas).
- `determinism`: 2 corridas idem producen mismo hash de reporte.
7. Reuso obligatorio: cada paso es funcion del registry (no inline en la app).
## Aceptacion
- Reporte hash-estable entre corridas con misma seed.
- 2 strategies de referencia (0088e) corren contra >= 1 simbolo y producen reporte completo.
- Reporte importable desde `analysis/strategy_lab` para comparar estrategias.
## No-objetivos
- Optimizacion de parametros (grid search, bayesian) — saldria a sub-issue futuro.
- Walk-forward / cross-validation — futuro.
- UI rica de reporte — primero CLI + markdown.
@@ -0,0 +1,63 @@
---
id: "0088h"
title: "Trading: app `live_runner` (service, paper-first, broker real behind flag)"
status: pendiente
type: feature
domain:
- trading
scope: multi-app
priority: alta
depends: ["0088b", "0088c", "0088d", "0088e", "0088f", "0088g"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088h — Trading: app `live_runner` (service, paper-first, broker real behind flag)
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088b, 0088c, 0088d, 0088e, 0088f, 0088g
**Blocks:** 0088j (assertions consume execution log del runner)
## Problema
Necesitamos un service de larga duracion que corra una o varias estrategias en vivo. Master debe estar siempre desplegable; un broker real introduce riesgo. Patron: live_runner mergea con paper-only activo; el adapter de broker real entra detras de feature flag OFF en `dev/feature_flags.json` (`live-broker-binance`, etc.) y se activa por commit explicito tras paper-trading estable.
## Piezas
1. App service `projects/trading/apps/live_runner/`. Tag `service`.
2. Config en YAML (`runner.yaml`): lista de `{strategy_id, params_path, broker_id, account_id, instruments[], schedule}`.
3. Bucle del runner (cada N segundos o evento):
- `if is_halted(): sleep`.
- Fetch fresh market snapshot via `market_data` group.
- Cargar portfolio snapshot desde `portfolio_tracker`.
- Llamar `Strategy.decide` (pura).
- Aplicar `apply_risk_pipeline`.
- Para cada intent superviviente: `broker.place_order` + persistir intent en `operations.db`.
- Reconciliar fills cada ciclo. Discrepancia > epsilon → `halt_all_strategies(reason)`.
- Emitir snapshot equity a `portfolio_tracker.equity_snapshots`.
4. Hooks:
- Adapter broker resuelto por nombre (paper/binance/ib/alpaca). Brokers reales detras de feature flag OFF; intentar usarlos con flag OFF → error claro.
- Cualquier excepcion no controlada → `halt_all_strategies` + notificacion.
5. Tabla `operations.db` propia del runner con `executions` y `intents` (motivos de drop por risk).
6. `e2e_checks`:
- `build`, `smoke` (arranca con `broker_paper`, una strategy noop que emite 0 intents, 1 ciclo, sale OK).
- `kill_switch`: con `halt_all_strategies` previo, el runner no emite ordenes.
- `reconciliation`: ledger == broker_paper (siempre true para paper, pero el check existe).
7. Deploy target en `deploy_server` (paper-only inicial).
## Aceptacion
- Runner corre en paper 7 dias seguidos sin incidentes (criterio de cierre fuera del codigo, en el run real).
- Flag `live-broker-*` documentado en `dev/feature_flags.json` con `enabled: false`.
- `e2e_checks` verde en CI.
- `halt_all_strategies` desde CLI corta el flujo en < 1 ciclo.
## No-objetivos
- Multi-runner / orquestacion de multiples runners (un solo proceso por ahora).
- Estrategias adaptativas que cambian params en vivo.
@@ -0,0 +1,56 @@
---
id: "0088i"
title: "Trading: app `trading_journal` (reflection log)"
status: pendiente
type: feature
domain:
- trading
scope: app-scoped
priority: alta
depends: ["0088a", "0088d"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088i — Trading: app `trading_journal` (reflection log)
**Status:** pendiente
**Created:** 2026-05-14
**Type:** feature
**Parent:** 0088
**Depends:** 0088a, 0088d
**Blocks:** 0088j (assertions sobre adherencia a reflection)
## Problema
Sin post-mortem por trade y por sesion, "mejorar" es una ilusion. El journal fuerza la reflexion en momentos clave (close de trade, fin de sesion, dia de drawdown notable). Es la pieza de **skill autoiterado** propiamente dicha: no es codigo del runner, es el ledger psicologico/operativo del operador.
## Piezas
1. App `projects/trading/apps/trading_journal/` (Mantine UI + sqlite_api + Go service).
2. Schema:
- `sessions` (id, started_at, ended_at, market_context, mood_tag, notes).
- `trade_reflections` (id, trade_group_id (link a transactions), opened_at, closed_at, thesis, what_went_right, what_went_wrong, lessons, tags, screenshots_paths).
- `weekly_reviews` (id, week_iso, sharpe_week, drawdown_week, n_trades, adherence_score, lessons).
- `tags` (id, name, color).
3. Reglas de UX:
- **Forzar reflection** antes de marcar un trade_group como "closed". Si no se rellena, queda `closed_pending_reflection` y aparece destacado.
- Atajo desde Mantine para vincular un trade con N screenshots (drop en local_files/).
4. Funciones puras:
- `compute_adherence_score_py_finance(trades, plan_yaml) -> float` (mide cuanto de lo ejecutado coincide con el plan declarado).
- `aggregate_weekly_metrics_py_finance(trades, reflections) -> dict`.
5. `e2e_checks`: build + smoke + check de `force_reflection` (intentar cerrar sin reflection devuelve 422).
6. Capability group `journal`. Pagina madre `docs/capabilities/journal.md`.
## Aceptacion
- Cerrar trade sin reflection es imposible via UI/API.
- Adherence score computable y exportable.
- Weekly review autogenerado los domingos (cron) — opcional para cerrar el issue, no obligatorio.
## No-objetivos
- AI auto-resumiendo reflections — futuro.
- Sentiment analysis sobre notas.
@@ -0,0 +1,59 @@
---
id: "0088j"
title: "Trading: wiring del bucle reactivo (assertions + proposals)"
status: pendiente
type: feature
domain:
- trading
- frontend
scope: multi-app
priority: alta
depends: ["0088d", "0088g", "0088h", "0088i"]
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0088j — Trading: wiring del bucle reactivo (assertions + proposals)
**Status:** pendiente
**Created:** 2026-05-14
**Type:** infra
**Parent:** 0088
**Depends:** 0088d, 0088g, 0088h, 0088i
**Related:** 0068 (e2e validation fase 4-5), 0069 (autonomous loop)
## Problema
Las 4 apps de trading (portfolio_tracker, backtester, live_runner, trading_journal) tienen cada una su `operations.db`, pero no hay assertions cruzadas ni proposals automaticas que cierren el ciclo MEJORAR. Sin este wiring, el bucle reactivo se queda en EJECUTAR/RECOPILAR y no impulsa cambios.
## Piezas
1. Assertions declaradas (kind libre, ver `assertions.md`):
- `live_drawdown_under_X` (critical): si `max_drawdown_live > config.max_dd_pct` → halt + proposal.
- `sharpe_live_vs_backtest` (warning): `|sharpe_live - sharpe_backtest_reference| > 0.5` → proposal "investigar drift".
- `slippage_realized_under_modeled` (warning).
- `reflection_adherence` (critical para weekly review): `reflections_done / trades_closed < 0.8` → halt suaves (no acepta nuevos trades hasta cubrir).
- `reconciliation_clean` (critical): ledger == broker.
- `consecutive_losing_days` (warning).
2. `e2e_checks` por app declarados (cada sub-issue ya los pide; este issue verifica que cumplen el contrato 0068).
3. Proposals automaticas via `fn-mejorador`:
- Cuando una assertion critical falla, abrir proposal con evidencia (assertion_ids + execution_ids + sample trades).
- Tipos esperados: `pause_strategy`, `tune_param`, `kill_strategy`, `add_filter`.
4. Integracion con `fn-orquestador` (issue 0069): tarea autonoma `trading-skill-loop` que cada noche corre fn-analizador sobre las 4 apps, agrega resultados y abre proposals si hay rojos.
5. Dashboard tab `trading` en `registry_dashboard` (fn_monitoring) con KPIs en vivo: equity, drawdown, sharpe, adherence, n_open_proposals.
6. Cron / Dagu DAG que orquesta:
- Diario: snapshot equity + assertions + proposals.
- Semanal: weekly_review + adherence + ranking estrategias.
## Aceptacion
- 1 falla simulada de cada tipo de assertion genera 1 proposal con evidencia.
- Dashboard muestra los 5 KPIs en vivo.
- `fn-orquestador` puede correr el bucle entero sin intervencion humana (modo paper).
- Halt automatico funciona end-to-end (simulado).
## No-objetivos
- Auto-apply de proposals. El humano (o flujo aprobado del 0069) sigue siendo gate para cambios de codigo o de risk_config.