From 097e764f6ad7d84acbc8c98b8089134c057c7425 Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Tue, 9 Jun 2026 08:40:11 +0000 Subject: [PATCH] feat(research): comando CLI research-trigger + flag force - option_chain_research_cycle: parametro force=True bypassa il gate enabled, per testare la raccolta prima di accendere il cron. - CLI `option-chain research-trigger`: esegue UNA volta il collector full-chain (source='research', book_depth popolato), come il gemello `trigger` per il collettore live. Verificato: 324 quote ETH su 7 scadenze (DTE 1..80), book_depth su tutte. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/cerbero_bite/cli.py | 64 +++++++++++++++++++ .../runtime/option_chain_research_cycle.py | 8 ++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/cerbero_bite/cli.py b/src/cerbero_bite/cli.py index ddc4164..186208c 100644 --- a/src/cerbero_bite/cli.py +++ b/src/cerbero_bite/cli.py @@ -1204,6 +1204,70 @@ def option_chain_trigger( ) +@option_chain.command(name="research-trigger") +@click.option( + "--strategy", + "strategy_path", + type=click.Path(exists=True, dir_okay=False, path_type=Path), + default=_DEFAULT_STRATEGY_PATH, + show_default=True, +) +@click.option( + "--db", + "db_path", + type=click.Path(dir_okay=False, path_type=Path), + default=_DEFAULT_DB_PATH, + show_default=True, +) +@click.option( + "--audit", + "audit_path", + type=click.Path(dir_okay=False, path_type=Path), + default=_DEFAULT_AUDIT_PATH, + show_default=True, +) +@click.option( + "--token", + type=str, + default=None, + help="MCP bearer token (override su CERBERO_BITE_MCP_TOKEN).", +) +@click.option("--asset", default="ETH", show_default=True) +def option_chain_research_trigger( + strategy_path: Path, + db_path: Path, + audit_path: Path, + token: str | None, + asset: str, +) -> None: + """Esegue UNA volta il collector research full-chain (source='research'). + + Cattura tutte le scadenze ed entrambe le ali con book_depth_top3 + popolato. ``force=True``: gira anche se ``research_collector.enabled`` + è false, utile per testare prima di accendere il cron orario. + """ + from cerbero_bite.runtime.dependencies import build_runtime # noqa: PLC0415 + from cerbero_bite.runtime.option_chain_research_cycle import ( # noqa: PLC0415 + collect_option_chain_research, + ) + + cfg = load_strategy(strategy_path).config + ctx = build_runtime( + cfg=cfg, + endpoints=load_endpoints(), + token=load_token(value=token), + db_path=db_path, + audit_path=audit_path, + bot_tag=load_bot_tag(), + ) + n = asyncio.run(collect_option_chain_research(ctx, asset=asset, force=True)) + console.print( + f"[green]Persisted {n} research quote(s) for {asset}[/green]" + if n > 0 + else "[yellow]No quotes persisted (chain empty or fetch failed)[/yellow]" + ) + + @option_chain.command(name="analyze") @click.option( "--strategy", diff --git a/src/cerbero_bite/runtime/option_chain_research_cycle.py b/src/cerbero_bite/runtime/option_chain_research_cycle.py index e90a2e1..fac3cf0 100644 --- a/src/cerbero_bite/runtime/option_chain_research_cycle.py +++ b/src/cerbero_bite/runtime/option_chain_research_cycle.py @@ -70,12 +70,16 @@ async def collect_option_chain_research( asset: str = "ETH", now: datetime | None = None, batch_size: int = DEFAULT_BATCH_SIZE, + force: bool = False, ) -> int: """Collect + persist un singolo snapshot full-chain ``research`` per ``asset``. Ritorna il numero di quote persistiti (0 su fallimento - best-effort o se il collettore è disabilitato).""" + best-effort o se il collettore è disabilitato). + + ``force=True`` bypassa il gate ``enabled`` (usato dal trigger CLI per + testare la raccolta prima di accendere il cron schedulato).""" rc = getattr(ctx.cfg, "research_collector", None) - if rc is None or not rc.enabled: + if rc is None or (not rc.enabled and not force): return 0 when = (now or datetime.now(UTC)).astimezone(UTC)