app.services.strategy_observability_service module

Observability aggregation service.

Reads from the same strategyexecutionlog table the engine writes via DslExecutionObserver and shapes it into the metrics view the dashboard renders for a single strategy, plus the A/B comparison endpoint that runs the same aggregation against two ids and computes deltas.

The repository already does the heavy lifting (SUM/COUNT/GROUP BY in the DB); this service is responsible for:

  • Combining 5 narrow queries into one response - the dashboard fetches once per page load.

  • Computing percentiles + histograms in Python from a bounded sample, because SQLite (used in tests) doesn’t have percentile_cont and PostgreSQL’s window-function variant is expensive on tables with millions of rows.

  • Tenant scoping: callers pass the strategy id, we resolve it via StrategyDefinitionService.get_strategy which 404s on cross-realm probes, so this service is implicitly tenant-aware.

class app.services.strategy_observability_service.StrategyObservabilityService(execution_log_repository, strategy_definition_service)[source]

Bases: object

Single entrypoint for the dashboard view + A/B view.

Parameters:
async get_metrics(*, id, realmId, sinceDt=None, untilDt=None)[source]

Build the per-strategy metrics card.

Resolves the strategy first so the response includes name + version + status - and so cross-realm probes 404 before we spend a query on the execution log.

Parameters:
  • id (str)

  • realmId (str | None)

  • sinceDt (datetime | None)

  • untilDt (datetime | None)

Return type:

StrategyMetricsResponse

async compare(*, idA, idB, realmId, sinceDt=None, untilDt=None)[source]

Run the metrics aggregation twice in parallel-ish (awaited sequentially since each takes ~5 narrow queries) and compute deltas server-side. Both ids are validated through the same get_strategy path so neither can leak from a foreign realm.

Parameters:
  • idA (str)

  • idB (str)

  • realmId (str | None)

  • sinceDt (datetime | None)

  • untilDt (datetime | None)

Return type:

StrategyComparisonResponse