jgrusewski 8a63f69130 fix(factory): closure hardening — zero-sum HRP guard, read-only dry-run, single trial-count authority, funnel + E501
I1 (crash): zero-sum HRP ZeroDivisionError — `hrp_sum = sum(...) or 0.0` was a no-op since
`0.0 or 0.0 == 0.0`. Guard BOTH divide sites in `_size_active`: all-allocated branch and
mixed branch now fall back to equal-weight when `hrp_sum <= 0`.

I2 (honest dry-run): `factory-cycle --dry-run` previously ran the full discovery chain
(gather+hunt+forward) and wrote to the store (mark_seen, upsert DISCOVERED, set_status
RETIRED). Gate the entire GENERATE→JUDGE→PROMOTE→PERSIST block behind `if execute:`.
In dry-run, only the FactoryLoop runs in kill-gated mode (promotes nothing) and reports
the current pool state without any store writes.

M1 (double-count + dedup namespace split): analyst fleet AND hunt each called `add_trials`,
and used different key formats (`market:kind:k=v` vs `kind|market|params`). Fix:
- `candidate_key(market, kind, params)` added to `fxhnt.domain.factory` as the single
  canonical key helper (format: `market:kind:k1=v1,k2=v2` — params sorted, 10g floats).
- `Proposal.key()` delegates to `candidate_key` (unified namespace).
- `FleetOrchestrator.hunt` uses `candidate_key` for its `cand_key` (was `kind|sym|params`).
- `AnalystFleet.gather` is now read-only: no `add_trials`, no `mark_seen`. The hunt is the
  single counting authority. gather only filters proposals already in `store.is_seen`.

M2 (funnel double-count): `forward` was sampled BEFORE `loop.allocate_and_promote()` so
sleeves promoted this cycle appeared in both `forward` and `deployed`. Now `forward` is
computed as `fwd_report.forward_tracking - len(res.promoted)` after promotion.

M3 (E501): wrap `assemble` signature in `regime_execution.py` (was 123 chars).

Pre-existing lint (UP042, E501) in `domain/factory/models.py` also fixed:
`StrategyStatus(str, Enum)` → `StrategyStatus(StrEnum)`;
`forward_returns` comment moved above the field to fit in 120 chars.

Tests: 246 pass (was 241). New tests in test_regime_execution.py:
  test_hrp_zero_weights_all_allocated_no_crash
  test_hrp_zero_weight_single_leg_no_crash
  test_hrp_zero_weights_mixed_branch_no_crash

Updated test_analyst_fleet.py: dedup-only contract assertions (trials==0, seen==0 after gather;
fresh proposals not pre-emptively marked seen; canonical key format check).

Updated test_factory_cycle_wiring.py: aligned with M1 — gather is read-only, hunt is the
single counting authority.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 21:27:23 +02:00

fxhnt

Agentic strategy-research & multi-strategy execution platform. It systematically discovers, backtests and out-of-sample-validates trading strategies across many markets, keeps only what survives a rigorous statistical gauntlet, and runs the survivors live (multiple strategies at once).

The bet is not a secret edge — it's breadth + discipline + automation. The hard part (and the moat) is refusing to fool yourself at scale; the validation gauntlet is the core, built and proven first.

Architecture (hexagonal / ports-and-adapters)

src/fxhnt/
  domain/        pure logic: gauntlet (Deflated Sharpe), strategies, backtest, models   ← no I/O
  ports/         contracts: DataProvider, repositories (the only seams)
  adapters/      infra: yahoo data, SQLAlchemy (Postgres/SQLite) + DuckDB stores
  application/   use-case services (ResearchService) — orchestrate via ports
  cli.py         composition root (wires concrete adapters)

See docs/architecture/0001-architecture.md.

Quickstart

pip install -e ".[dev]"

pytest                                   # unit (gauntlet falsification) + integration (vertical slice)
fxhnt strategies                         # list strategy kinds
fxhnt research SPY --kind trend --window 200    # data → backtest → gauntlet → persist
fxhnt list --passed-only                 # the survivor library

Config via FXHNT_* env vars (e.g. FXHNT_OPERATIONAL_DSN=postgresql+psycopg://...). Defaults to SQLite + a local DuckDB file under ~/.fxhnt/.

Status

Vertical slice working: data (Yahoo) → strategy (trend) → backtest (net of costs) → IS/OOS gauntlet → persistence (operational + analytical). Next: the multi-strategy execution layer, more strategy templates + data adapters, and the agentic discovery search on top of the proven gauntlet.

Description
No description provided
Readme 10 MiB
Languages
Python 96.9%
HTML 2.3%
HCL 0.5%
Shell 0.3%