Metadata-Version: 2.4
Name: aetherion-ai
Version: 1.5.3
Summary: Aetherion - autonomous market intelligence: a multi-agent research, risk-controlled paper-trading system with a terminal UI and web dashboard.
Author: Amit Singh
License: All rights reserved
Project-URL: Homepage, https://github.com/Amits64/aetherion
Project-URL: Repository, https://github.com/Amits64/aetherion
Project-URL: Issues, https://github.com/Amits64/aetherion/issues
Keywords: trading,agents,llm,langgraph,quantitative,paper-trading,terminal-ui
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: scipy>=1.10
Requires-Dist: scikit-learn>=1.3
Requires-Dist: matplotlib>=3.7
Requires-Dist: seaborn>=0.12
Requires-Dist: pyyaml>=6.0
Requires-Dist: requests>=2.31
Requires-Dist: httpx>=0.27
Requires-Dist: python-dotenv>=1.0
Requires-Dist: yfinance>=0.2.40
Requires-Dist: backtrader>=1.9.78
Requires-Dist: rich>=13.4
Requires-Dist: textual>=0.80
Requires-Dist: fastapi>=0.100
Requires-Dist: uvicorn>=0.23
Requires-Dist: websockets>=12.0
Requires-Dist: pydantic>=2.0
Requires-Dist: langgraph>=1.2.6
Requires-Dist: langchain-core>=1.4.8
Requires-Dist: litellm>=1.0
Provides-Extra: crypto
Requires-Dist: python-binance>=1.0; extra == "crypto"
Requires-Dist: pycoingecko>=3.1; extra == "crypto"
Provides-Extra: india
Requires-Dist: dhanhq>=2.0; extra == "india"
Provides-Extra: cache
Requires-Dist: redis>=4.5; extra == "cache"
Provides-Extra: finnhub
Requires-Dist: finnhub-python>=2.4; extra == "finnhub"
Provides-Extra: analytics
Requires-Dist: empyrical>=0.5.5; extra == "analytics"
Provides-Extra: crew
Requires-Dist: crewai>=0.30; extra == "crew"
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: pytest-mock>=3.11; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"

<p align="center">
  <img src="https://lh3.googleusercontent.com/d/1WPZZ6r6eXOeWxYul0UjSryy5iBM7RefE" alt="AETHERION brand icon" width="160" />
</p>

<div align="center">

# AETHERION — Autonomous Market Intelligence

**Observe. Debate. Quantify. Protect.**

An evidence-driven, multi-agent market intelligence system built around quantitative research, disciplined risk controls, and continuous reflection.

</div>

An agentic trading research and **paper-trading** platform. A LangGraph pipeline runs an
analyst → bull/bear debate → risk committee → execution cycle over a screened universe
(crypto, US/India equities, commodity futures), with LLM agents kept **advisory** behind
deterministic risk controls. A rule-based, validated baseline strategy (time-series
momentum) runs alongside the agents. A FastAPI backend streams everything to a React
dashboard.

> **Status: research / paper trading.** Nothing here is financial advice, and nothing is
> wired to trade real money by default. Most of what the research in this repo found is
> *negative* (see [Research findings](#research-findings)) — the value of the system is
> measurement honesty and risk control, not a claimed edge.

---

## How a cycle works

```
memory → universe (screener) → market_data → regime
      → technical ┐
      → fundamental├→ bull → bear → research_manager → trader ┐
      → sentiment  │                                          ├→ risk committee
      → macro     ┘                          baseline (TSMOM) ┘   (aggressive / neutral / conservative)
      → risk_manager → portfolio_manager → human_approval → execution → reflection
```

- **Agents are advisory.** Every LLM/skill call has a deterministic fallback, and a per-cycle
  wall-clock budget (`llm_time_budget_s`, default 270 s) bounds latency when providers degrade.
- **Hard limits are deterministic.** The risk manager, execution node and position manager
  enforce limits regardless of what an agent says.
- **Two LLM paths.** The real Hermes Agent CLI (tools: web,
  Yahoo Finance MCP, …) when available, falling back to an in-process call to the
  `aetherion/skills/*/SKILL.md` prompts through OpenRouter (free-model pool) → Ollama cloud → local Ollama.

## Real-time layer

The LLM cycle runs every ~30 minutes (it takes minutes per pass), but everything deterministic follows the
tape in seconds over the shared `/ws/live` WebSocket:

| Event | Cadence | What it carries |
|---|---|---|
| `price_tick` | 2 s | Latest tick per watched symbol (Binance WSS for crypto, Yahoo WSS for equities/futures) |
| `feed_status` | 2 s | `live` / `closed` / `stalled` / `polling` per symbol, so a quiet chart is never ambiguous |
| `ta_update` | 5 s | Technical read recomputed against the forming bar — same scoring as the agent cycle (`aetherion/graph/nodes/technical.technical_score`) |
| `pnl_update` + exits | 5 s with live ticks, else 30 s | Position marks and stop/target/trailing exits |

The chart keeps a forming candle (running high/low, new bar on rollover) and survives the 60 s refetch
without snapping back. **A closed market cannot tick:** gold, silver and crude (CME futures) are closed
from Friday 17:00 ET to Sunday 18:00 ET (and 17:00–18:00 ET daily), US equities outside 09:30–16:00 ET;
the dashboard shows `MARKET CLOSED` instead of a frozen chart. Calendars live in `aetherion/execution/market_hours.py`
(exchange holidays are not modelled; a holiday shows as `FEED STALLED`, which is accurate).

### Multi-timeframe context

`aetherion/analytics/mtf.py` builds one per-timeframe view (5m, 15m, 1h, 4h, 1d: direction, EMA trend, RSI/ADX, MACD,
swing structure, and the patterns detected on that timeframe's own candles) and a weighted alignment
(5m 0.5 · 15m 0.75 · 1h 1 · 4h 1.5 · 1d 2). The agent cycle, the real-time loop and the chart read the same
builder, and the bull/bear/research/trade-synthesis agents receive its compact summary through `slim_tech`.
`GraphConfig.mtf_timeframes` selects which timeframes vote in the technical score (a code-level field;
there is no API or env setting yet). `["1h","4h","1d"]` reproduces the pre-5m/15m technical score exactly:
count-vote alignment, the legacy (unscaled) pattern tilt and trader geometry, and the legacy 4h/1d row counts
(200/60). Volatility-scaled patterns appear only in the MTF context, the chart and the agents' summary. Pattern tolerances scale with each timeframe's own volatility;
calibration and before/after density are in `docs/research/pattern-calibration.md`
(`python scripts/calibrate_patterns.py --check` re-verifies it).

## Risk and reliability controls

| Control | Where | Behavior |
|---|---|---|
| Daily-loss kill switch | `aetherion/graph/nodes/execution.py` | Halts all orders when daily loss ≥ `max_daily_loss_pct`; optional book flatten |
| **Drawdown guard** | `aetherion/execution/risk_guard.py` | Persisted peak-to-trough halt of *new entries*, restart-proof daily baseline, loss-streak breaker, de-risk ladder (size only ever scaled down). Every halt has a cooldown and auto-resets; fails closed on bad data |
| **Extreme-shock gate** | `aetherion/analytics/shock.py` | Lagged-window return z-score + volatility expansion (+ volume confirmation): warning → half size, critical → block new entries. Closes are never blocked |
| Stale-data gate | `aetherion/graph/nodes/market_data.py` | Analysis-only when bars are stale (e.g. market closed) |
| Stale-entry guard | `aetherion/graph/nodes/execution.py` | Rejects an entry if live price drifted > `max_entry_deviation_pct` from the analyzed price |
| Position manager | `aetherion/execution/position_manager.py` | Stop/target, breakeven + trailing stop, time stop, flatten-all |
| Expectancy throttle | `aetherion/analytics/expectancy.py` | Halves size for setup buckets with ≥10 closed trades and negative mean R |
| Re-entry cooldown | `aetherion/graph/nodes/risk_manager.py` | Blocks immediate re-entry after a close |
| Strategy governance | `aetherion/strategies/governance.py` | Strategies must be registered/approved before live use |
| Calibration report | `scripts/calibration_report.py` | Tests whether stated trade confidence carries information (AUC permutation test, Brier skill, ECE) |

## Quick start

Requirements: Python ≥ 3.11. Optional: [Ollama](https://ollama.com) and the Hermes Agent CLI for the CLI LLM tier;
Node.js only if you work on the web dashboard.

### Install from PyPI

```bash
pip install aetherion-ai            # the command it installs is `aetherion`
aetherion                           # opens the terminal: pick symbols, press START
```

`aetherion` starts the backend for you, shows a symbol selector, and fades into **Mission Control** once trading
begins. Everything else:

```bash
aetherion --demo                    # synthetic engine: no backend, no API keys, nothing to configure
aetherion --attach                  # skip the selector, watch an engine that is already running
aetherion serve [--port 8001]       # only the API + web dashboard (http://127.0.0.1:8001)
aetherion doctor                    # check the installation, LLM keys and data folder
aetherion pipeline --help           # research / backtest command line
pip install "aetherion-ai[crypto,india,cache,analytics,crew]"   # optional integrations
```

**Ollama Cloud (no local model needed).** Pick *Ollama Cloud* as the provider, paste the key from your own Ollama
account (ollama.com/settings/keys; the free plan works, within Ollama's usage limits) and tick the hosted models you want
(`gpt-oss:120b`, `gpt-oss:20b`, `glm`, `deepseek`, ... fetched live; the `-cloud` spelling is accepted too). The first
ticked model is preferred; the others are fallbacks, and a model that hits its usage limit is skipped automatically.
Models installed on your PC are never used unless you choose the *Ollama (models installed on this PC)* provider or set
`AETHERION_ALLOW_LOCAL_MODELS=1`.

**Choosing your model.** The launch screen has a *Provider / Model / API key* row (OpenRouter, Anthropic, OpenAI,
Gemini, Groq, DeepSeek, Mistral, local Ollama, or any OpenAI-compatible endpoint). Pick a provider, type or keep the
suggested model id (any [litellm](https://docs.litellm.ai/docs/providers) model works), paste your key, press
**TEST KEY** to check it, then START. The choice is saved: next time the screen opens on the same model with your
key shown masked, and you can switch to another provider whenever you like (each provider remembers its own model).
Keys are stored in `~/.aetherion/.env` (owner-only permissions) and the chosen default in `~/.aetherion/llm.json`; keys
are never logged, never put in `llm.json`, and only sent to a backend running on your own machine.

Configuration: environment variables, or a `.env` in the current folder or in `~/.aetherion/.env` (LLM keys such as
`OPENROUTER_API_KEY`; all optional for paper mode). State, logs and memory live in `~/.aetherion/`.

> The PyPI name `aetherion` belongs to an unrelated project, so the package is published as **`aetherion-ai`**.

Aetherion follows [Semantic Versioning](https://semver.org); releases are derived from the commit history (`aetherion --version`, `CHANGELOG.md`, `docs/versioning.md`).

### From source

```bash
git clone git@github.com:Amits64/aetherion.git
cd aetherion

python -m venv .venv && source .venv/bin/activate      # Windows: .venv\Scriptsctivate
pip install -e ".[dev]"

cp .env.example .env        # then fill in the keys you need (all optional for paper mode)

aetherion                   # terminal UI (starts the backend itself)
aetherion serve             # or: API only on :8001
cd frontend && npm install && npm run dev      # dashboard with hot reload (separate terminal)
```

The dashboard is also served by `aetherion serve` at http://127.0.0.1:8001 once built (`python scripts/build_release.py`).
Pick symbols (or leave empty for the auto-screener) and press **Start**.
The default broker is an in-memory **paper broker** that restarts at $100,000 each process.

If you use the Hermes CLI tier, run `ollama serve` first. When the CLI's provider is
unreachable the client skips it instantly and uses the in-process fallback.

The Hermes CLI runs skills from **its own registry** as `aetherion-<name>`, not from this repo's `aetherion/skills/`
folder. The repo is the source of truth; register (or refresh) the 13 skills the pipeline uses with:

```bash
python scripts/register_hermes_skills.py --check     # is Hermes up to date with aetherion/skills/?
python scripts/register_hermes_skills.py             # write/refresh the aetherion-* copies
```

(`--dest DIR` overrides the Hermes skills directory; other skills there are never touched.)

### Command line

```bash
aetherion pipeline --help      # data, strategies, backtest, validate, optimize, regime, paper, performance, live
aetherion pipeline strategies --help # strategy management
python scripts/calibration_report.py          # is trade confidence informative? (needs closed trades)
```

## Configuration

Runtime behavior is controlled by `GraphConfig` (`aetherion/graph/state.py`); the backend builds it from the
dashboard request. Notable knobs:

| Setting | Default | Meaning |
|---|---|---|
| `max_daily_loss_pct` | 0.02 | Daily kill-switch threshold (fraction) |
| `max_drawdown_pct` | 0.10 | Peak-to-trough halt of new entries (fraction) |
| `max_loss_streak` | 4 | Consecutive losing trades before a cooldown halt |
| `shock_gate_enabled` | True | Extreme-shock gate on new entries |
| `risk_per_trade_pct` / `max_position_pct` | 0.01 / 0.20 | Sizing |
| `baseline_strategies` | `["tsmom"]` | Deterministic baseline layer (empty list disables) |
| `llm_time_budget_s` | 270 | Per-cycle LLM wall-clock budget |
| `kill_switch_flatten` | False | Also close the book on a daily-loss breach |

Environment variables are listed in [`.env.example`](.env.example) (names only — never commit `.env`).
Useful switches: `COMMODITY_DATA_SOURCE` (`yfinance` default | `binance`), `HERMES_CLI_CONCURRENCY`,
`HERMES_CLI_PROVIDER_URL`, `OLLAMA_*`, `OPENROUTER_*`, `TRADING_MODE`.

**Data providers** (`aetherion/data/providers/`): Binance, Yahoo Finance, CoinGecko, FRED, Dhan (NSE/BSE),
local CSV, with an on-disk/in-memory cache and a negative cache for failing symbols. Gold/silver/crude
futures (`GC=F`, `SI=F`, `CL=F`) use Yahoo (COMEX) by default so analysis, live price patching and the
stale-entry guard all see one instrument; Binance USDT-M perpetuals (`XAUUSDT`, `XAGUSDT`, `CLUSDT`)
are available behind `COMMODITY_DATA_SOURCE=binance`.

**Sentiment & macro inputs** (no API keys needed): `aetherion/data/social.py` pre-fetches StockTwits (Bullish/Bearish
tags; GLD/SLV/USO cashtags stand in for gold/silver/crude futures) and Reddit RSS in code and hands them to
the sentiment skill as data, so a Hermes outage no longer means no social signal. A failed fetch is
reported as `unavailable`, never as silence. The report carries a six-band `sentiment_band`
(`Mixed` = sources disagree), a deterministic `divergence` flag, `contrarian_extreme` (>=90/10 crowd) and
`data_gaps`. `aetherion/data/macro_feed.py` adds FRED rates (10y, real 10y, fed funds, 10y-2y, CPI, broad dollar via
the keyless `fredgraph.csv`) with a `metals_pressure` read, and Polymarket event odds (Fed cuts, recession,
gold/oil/bitcoin). The approach follows TauricResearch/TradingAgents' analysts; see
`docs/research/tradingagents-analyst-comparison.md`.

## Terminal UI (replaces raw logs)

A Bloomberg-style Textual terminal that shows what the agents are doing instead of Python log lines.

```
aetherion                       # opens the launcher; the backend is started (and stopped) for you
aetherion --attach              # attach to an engine already running at http://localhost:8001 (--url, --api-key)
aetherion --demo                # no backend at all
AETHERION_QUIET_LOGS=1 aetherion serve    # engine only; console shows warnings, full log -> ~/.aetherion/logs/aetherion.log
```
Run the engine and the terminal in two shells; the terminal attaches to a running engine and can be closed and reopened freely.

**Mission Control** - six screens (keys `1`-`6`): **1 Mission Control** (8-panel mosaic: Portfolio Command Center,
Neural Agent Network, Active Agent Grid, AI Thinking Feed, Risk Matrix, Opportunity Scanner, Execution Queue, Reflection
Memory), **2 Agent Tree** (the cycle as a collapsible tree: cluster → agent → every skill/LLM call, with a detail pane showing the full result text; `x`/`z` expand/collapse all, `s` newest-first; the Mission tab keeps the card grid. Each call is also its own card there: `Research Agent #01, #02, #03` with start time, runtime, tokens,
symbol, confidence and a result summary; running cards pulse, finished ones show an animated check, failures flash red),
**3 Timeline** (every execution in chronological order with a Gantt bar, so you see how a decision was formed),
**4 Neural Net** (Memory, Research, Technical, Risk, Approval, Execution, Reflection as connected nodes; packets travel
along the links when information flows), **5 Intel**, **6 LLM**, **7 Portfolio & Risk** (the side columns at full size - use it when the window is too narrow to show them on the Mission tab). Keys: `s` sort cards, `f` filter thoughts, `c` clear
the activity strip, `q` quit. An animation clock (12 Hz) re-renders only the panels that have motion.

```
graph nodes --wrap_node--> EventBus <--publish-- HermesClient.run_skill / maybe_invoke_model
                              |                         (observability/)
                              +--> app_state.push_event --> /ws/live --> terminal.client --> terminal.store.reduce --> widgets
GET /api/events/recent  <-- replay buffer (late attach backfill)       REST pollers: portfolio, status, risk, llm-health, assets
```

`aetherion/observability/` holds the schema (21-agent registry), the thread-safe bus and the humanizer ("Risk Agent approved 1,
rejected 3 (confidence x2, shock block)"); every graph node is wrapped once in `aetherion/graph/build.py`. `aetherion/terminal/store.py` is a
pure reducer, so the whole data layer is unit-tested without a UI.

## Multi-asset correlation

`aetherion/analytics/correlation.py` + the **Correlation** dashboard page (`/api/correlation`, `/api/correlation/analyze`).
Correlations are on log *returns* (never price levels) over the timestamps both assets traded, so a crypto Fri->Mon
move is compared with an equity Fri->Mon move. Per pair: short/mid/long windows with the short-vs-long change
(regime breaks, sign flips), stress correlation (worst 25% of days), rolling series, lead-lag and beta to SPY.
Basket level: hierarchical clusters, average pairwise correlation, first-PC share, effective independent bets
(also for your open positions), and a regime block (rolling mean correlation, correlation entropy, cross-asset
volatility dispersion, tail kurtosis/skew; adapted from an earlier standalone analyzer). Intervals 1d/4h/1h/15m.
"Analyze with AI" runs the Hermes `correlation-analysis` skill on a compact numeric digest and falls back to a
deterministic analysis in the same schema when no LLM answers.

## Neural-network signal filter (opt-in)

Any strategy can be wrapped with a small NumPy network that scores its BUY signals (meta-labeling): set
`"nn_filter": true` in the strategy params (API/CLI/`create_strategy`). The strategy still decides *when* a signal
exists; the network, trained only on that strategy's own past signals and their triple-barrier outcomes, vetoes
those with P(win) below `nn_min_prob`. It fails open: until it has `nn_min_signals` resolved signals **and** its
holdout AUC's 95% lower bound exceeds 0.5, the base strategy trades unchanged. SELLs (exits) always pass.
Code: `aetherion/analytics/meta_label.py`, `aetherion/strategies/nn_overlay.py`; A/B harness: `scripts/eval_nn_overlay.py`.
**Measured result (BTC/ETH/GC=F 1h, 3000 bars, walk-forward): no reliable improvement** (see
`docs/research/nn-overlay-ab.md`), so it stays off by default.

## Outcome-settled memory

Every trade idea (executed or not) is logged as a *pending* decision in `~/.aetherion/memory/decision_outcomes.jsonl`.
Once `outcome_horizon_hours` (24h) has traded, the next cycle's memory node settles it against realised prices:
directional return, R-multiple (from the stop) and alpha vs a benchmark (BTC for altcoins, SPY for equities,
none for BTC/commodities). A two-sentence lesson that cites the number is written (LLM when the budget allows,
deterministic otherwise) and the per-symbol track record plus recent lessons lead `memory_context`, point-in-time
(only outcomes already known). Design from TauricResearch/TradingAgents' memory log; code in
`aetherion/graph/outcome_memory.py` and `aetherion/graph/outcome_loop.py`. Settings: `outcome_memory_enabled`,
`outcome_memory_path`, `outcome_horizon_hours`, `outcome_max_reflections`.

## Strategies

Registered in `aetherion/strategies/implementations/`: `tsmom` (deployed baseline), `smart_money`,
`trend_following`, `mean_reversion`, `chart_patterns`, `neural_network`, `quantum_resonance`.
The backtest engine (`aetherion/strategies/backtest.py`, Backtrader) and walk-forward tools
(`aetherion/validation/walk_forward.py`) back the evaluations below.

## Research findings

Summary of the evaluations behind the design choices (details in
[`docs/research/strategy-findings.md`](docs/research/strategy-findings.md)):

| Question | Result |
|---|---|
| Does time-series momentum (long above SMA200) work? | **Yes, as drawdown control** — on a diversified basket Sharpe ≈ buy-and-hold with a 27.7% vs 67% max drawdown. Deployed as the baseline. Single-symbol results are weak; the value is the basket |
| Do per-symbol tuned strategies hold up out-of-sample? | **No** — full-sample winners collapsed walk-forward (curve-fits) |
| Gold / silver / crude: any tradeable edge beyond trend risk-reduction? | **No** — shorts lose on every family; long/flat trend never beats buy-and-hold Sharpe; ratio mean-reversion and cross-sectional momentum ≈ 0 |
| Can neural nets predict daily direction? | **No** — pooled walk-forward AUC ≈ 0.50 for logistic/MLP/gradient boosting |
| Can ML forecast volatility? | Yes (R² 0.67 vs 0.59 trailing) — but the trading gain is negligible (+0.01–0.03 Sharpe), so it is not deployed |
| Do chart patterns work standalone? | **No** — kept only as confluence |

## Testing

```bash
pytest                      # 480 tests
pytest tests/unit/test_risk_guard.py -v
```

Tests isolate the expectancy ledger and the risk-guard state (`tests/conftest.py`) so they never
touch `~/.aetherion`. Safety controls are test-driven: each has a test proving it fires, plus mutation
checks that fail when the control is broken.

## Repository layout

```
backend/            FastAPI app (REST + /ws/live), starts/stops the trading loop
frontend/           React + Vite dashboard (Framer Motion)
graph/              LangGraph pipeline: build.py, state.py, nodes/ (one module per stage), utils/
skills/             Agent skill prompts (SKILL.md): source of truth for Hermes (via register_hermes_skills.py) and the in-process fallback
strategies/         Strategy base/registry/governance, backtest engine, implementations/
execution/          Brokers (paper, Dhan), position manager, risk_guard, live tick streams
analytics/          Expectancy ledger, shock gate, calibration, regime, performance, patterns
portfolio/          Optimizer, covariance estimators, risk models
validation/         Walk-forward validation, deflated-Sharpe helpers
data/               Data pipeline + providers + cache
crew/, miaoxiang/   Auxiliary orchestration and backtest helpers
config/             costs.yaml, risk.yaml, settings.yaml
scripts/            calibration_report.py, register_hermes_skills.py, TradingView shortlist, MCP patch helpers
examples/           Small standalone examples
docs/               Research findings, design notes, specs/plans, archive/
tests/              unit/ and integration/
hermes_client.py    Hermes CLI + in-process LLM fallback client
llm_config.py, llm_rotation.py   LLM backends and the free-model rotation pool
research_*.py       Scheduled research briefings (see below)
```

### Scheduled research scripts

`research_hourly.py` (and the helpers `research_hourly_news.py`, `research_briefing.py`) are run
hourly by a Windows Task Scheduler job — see
[`docs/research/HOURLY_RESEARCH_SETUP_SUMMARY.md`](docs/research/HOURLY_RESEARCH_SETUP_SUMMARY.md).
They stay at the repository root because the scheduled task references that path; moving them
requires updating the task.

## Known limitations

- Paper broker state is in-memory and resets per process; the guard's saved state is scoped to the
  broker session so it never mistakes a reset for a drawdown.
- Free-tier LLM providers are rate-limited; the per-cycle budget and fallbacks exist for that reason.
- Commodity futures are continuous (unadjusted-roll) series from Yahoo; volume on `GC=F`/`SI=F` is
  noisy around contract rolls, so volume is only used to *confirm* price-based shock signals.
- No license has been specified.
