Skip to content

Reports

Arye Kogan edited this page Sep 23, 2025 · 1 revision

Reports

Each pipeline run writes machine-readable (daily_report.json) and human-readable (daily_report.html) summaries under reports/<as-of>/. Reports aggregate holdings, risk alerts, proposal status, and a manifest of upstream artifacts so operators can audit every number.

Anatomy of the daily report

  • Header – timestamp, base currency, and run context (as-of date and generation time).
  • Portfolio snapshot – position-level table with quantity, price, value, weight, cost basis, unrealised P&L, and rolling returns.
  • Risk summary – crash/drawdown alerts, market filter status (RISK_ON/RISK_OFF), and benchmark rule outcome.
  • Actions & orders – proposed orders and exit recommendations when the rebalance pipeline populates them.
  • Signals overview – ranked signal table (rebalance pipeline only).
  • Performance metrics – rolling metrics such as 63d Sharpe, 20d return, holdings count.
  • Artifact manifest (“QA box”) – SHA256 hashes and absolute paths for every artifact referenced by the report.
  • Notes – warnings about missing inputs (e.g., absent proposals during a daily-only run).

Sample output

The repository includes a reference run:

reports/2024-09-23/
├── daily_report.html
├── daily_report.json
├── manifest.json
├── risk_alerts.json
└── run.log

Excerpt from daily_report.json:

{
  "as_of": "2024-09-23",
  "portfolio": {
    "value": 38141.37,
    "cash": 10000.0,
    "positions": [
      {
        "symbol": "SPY",
        "quantity": 50.0,
        "price": 562.83,
        "value": 28141.37,
        "weight": 0.7378,
        "cost_basis": 420.0,
        "unrealized": 7141.37,
        "unrealized_pct": 0.3401,
        "ret_20d": 0.019
      }
    ]
  },
  "risk": {
    "market_state": "RISK_ON",
    "benchmark": "SPY",
    "passed": true,
    "alerts": []
  },
  "actions": {
    "status": "UNKNOWN",
    "orders": [],
    "notes": []
  }
}

The corresponding HTML presents the same data with tables and styling; copy reports/2024-09-23/daily_report.html into a browser to inspect the rendered view.

Interpreting risk flags and thresholds

  • Crash alerts fire when ret_1drisk.crash_threshold_pct (default −8%).
  • Drawdown alerts fire when (close / rolling_peak) - 1risk.drawdown_threshold_pct.
  • Market filter state is RISK_ON when the benchmark rule (e.g., close > sma_200) returns true, otherwise RISK_OFF.
  • Alerts include a human-readable reason string with threshold and observed value. Treat any alert as an immediate review item.

Tuning report behaviour

  • Adjust formatting thresholds by editing ReportBuilder filters or injecting a custom Jinja template if you need alternate branding.
  • Change indicator sets by updating the preprocessor (src/trading_system/preprocess.py) and referencing the new columns in templates.
  • Modify proposal messaging (e.g., exit rationale, order wording) in ReportBuilder._build_actions_section.

Working with artifacts

  • manifest.json lists hashes for config, holdings, data_raw, data_curated, risk_alerts, and the report payload itself. Validate it with poetry run ts observability manifest --run reports/<as-of>.
  • run.log contains structured JSON lines for each step (pipeline_start, step_start, step_completed, etc.). Stream it with poetry run ts observability tail --run reports/<as-of>.
  • Pair daily_report.json with risk_alerts.json and rebalance_proposal.json to automate downstream QA workflows.

For troubleshooting missing sections or blank tables, jump to Troubleshooting.

Clone this wiki locally