Skip to content

The factory

The factory is cents running itself. It walks a universe of symbols, asks the orchestrator who’s interesting, opens paper theses for those that clear an entry threshold, watches them, and closes them on target / stop / horizon / premise-invalidation.

This page covers what it is and how it thinks. For the philosophical backdrop — why we built it this way — read Operating principles.

The factory’s goal is not maximizing P&L. It’s producing a labeled outcomes dataset stratified by cohort, regime, and orchestrator arm, so that after enough resolved theses you can mine the data for which kinds of trades cents generates well under which conditions. The income algorithm comes out of that map; it doesn’t exist yet, and shouldn’t, until the data supports a real conclusion.

Three commitments shape the structure:

  • The neutral cohort is mandatory (paired mode is the default). Without it you can’t tell whether the directional cohort earned skill or rode regime beta.
  • The random arm is mandatory (cents factory run --orchestrator random). Without a matched-cadence baseline, you can’t tell whether the LLM arm produced signal or just rode whatever the universe was doing while it ran. Every thesis carries orchestrator_label so cohort analytics can compare the arms directly.
  • Premise tags are first-class. Every thesis declares which regime variables it depends on, drawn from the same controlled vocabulary the EventAgent uses to tag policy events. That shared vocab is what makes premise-invalidation mechanical.

The engine records, it does not gate. Drawdown, liquidity, borrow, beta-matched hedging, and calibrated-p are all computed and stored on every thesis but never block an open. The point is the labeled outcomes dataset, not trading controls. cents/finance/* is a utility library for callers writing their own analytics, not a set of gates the engine relies on. See Scope.

Defaults from cents factory init:

FieldDefaultWhat it means
budget_usd100_000Max gross notional across all open factory positions
target_positions30Derives position_size_usd = budget / target = $3,333
entry_threshold7.0`
preemption_margin5.0New conviction must beat lowest open’s by this much to displace
cohort_modepairedpaired opens both legs (2× notional); directional_only opens one
sizing_modeequal_dollarbudget / target_positions per leg. Flip to vol_scaled to opt into inverse-vol sizing.
beta_match_hedgefalseHedge leg is dollar-matched. Flip to true to opt into 60-day OLS beta-matched sizing.
default_horizon_days30Auto-close as UNCLEAR after this many days
default_target_pct+10%Close as CORRECT when price moves this far in conviction direction
default_stop_pct-5%Close as INCORRECT when price moves this far against conviction
max_new_per_run5Rate-limit so one run can’t reshape the whole book
max_per_premise_tag2Concentration cap — max open theses sharing any single premise tag

So under defaults: $3,333 per leg; paired theses cost $6,667 each; the budget supports ~15 paired theses or ~30 directional. With max_per_premise_tag = 2, no more than 2 open theses ever depend on any single regime variable.

  • Symbol has no current factory thesis (the held-symbols set excludes both symbol and hedge_symbol)
  • Orchestrator’s |conviction_delta| ≥ entry_threshold
  • LLM-classified premise_tags don’t push any tag past max_per_premise_tag cap
  • Either budget covers it, OR a lowest-conviction open thesis can be preempted (and new conviction exceeds it by preemption_margin)
  • Within max_new_per_run for this run
  • (In paired mode) a sector-ETF hedge is resolvable (SPY fallback when unknown)
  • delta > 0 → LONG underlying (+ SHORT hedge in paired mode); target above entry, stop below
  • delta < 0 → SHORT underlying (+ LONG hedge in paired mode); target below entry (price has to drop to win), stop above
  • PREMISE_INVALIDATION alert fires (an ingested event’s tags intersect this thesis’s premise_tags) → outcome INVALIDATED
  • Price hits target → CORRECT
  • Price hits stop → INCORRECT
  • Horizon expires → UNCLEAR
  • A higher-conviction candidate preempts it → PREEMPTED (stratified out of win-rate analytics)

Symbols closed as INVALIDATED in this run are off-limits to the same-run open phase. Both the symbol and its hedge — so the invalidating event can’t reopen them before it ages out.

  • No real-money execution — paper positions only (see Scope)
  • No reinvestment / compounding — budget is a static notional cap
  • No active threshold auto-tuning — entry/preemption parameters are static config and freeze at cents experiment register time
  • No pyramiding — one open thesis per symbol at a time
  • No chained preemption — won’t unwind two cheap theses to fit one expensive new one
  • No backtest mode — runs only against now()
  • Does not gate on drawdown, liquidity, borrow, or calibrated-p. Those numbers are computed and stored on every thesis, but the engine never refuses to open on them. Trading-shaped behavior is opt-in via the cents/finance/ utilities.

Each of these is tracked as a follow-up in GitHub issues.

One full run on a 14-symbol universe: ~90 seconds, ~$0.02 in Anthropic spend. A daily event refresh adds ~$0.05 (one Haiku call per ingested event). At a daily cadence: roughly $20–35/year in operating cost, plus whatever Sentiment scoring you trigger separately. Visible end-to-end via cents usage summary.

You give the factory a hypothetical $100k budget; it spends it on small, premise-tagged, regime-snapshotted, direction-aware bets across the universe you defined. It tells you what closed, why it closed, and what the LLM cost you. After enough closes, you have a dataset that can answer questions like “do cents-generated theses tagged tariffs.china outperform those tagged ai_capex?” and “does the directional cohort actually beat the neutral cohort, or are we renting beta?” — and that’s the deliverable, not the P&L number on any one run.

Terminal window
cents factory init # scaffold ~/.cents/factory.toml
cents factory run --dry-run # preview proposed actions
cents factory run # LLM arm — the real loop
cents factory run --orchestrator random # control arm — uniform-random conviction, no LLM calls
cents factory run --orchestrator random --orchestrator-seed 42 # reproducible control arm
cents factory run --max-cost-usd 5.00 # pre-call cost cap; aborts before the offending call
cents factory status # current state, last run, budget util
cents factory analyze # cohort-stratified outcomes summary
cents factory analyze --by discovery,cohort,regime
cents factory analyze --by cohort,orchestrator # the 2x2 LLM-vs-random win-rate table
cents factory analyze --include-cost-per-outcome # adds llm_cost_per_{opened,judged,correct} per cell

The --by orchestrator axis stratifies by orchestrator_label (llm vs random). Pair it with --by cohort to read the experiment’s primary metric off the resulting 2×2 grid.

--include-cost-per-outcome is opt-in. It adds llm_cost_per_opened, llm_cost_per_judged, llm_cost_per_correct, and llm_cost_total_usd to every cell, plus an unattributable_cost_usd figure at the top level for LLM calls that didn’t bind to any thesis (random-arm cells naturally read $0). Attribution joins llm_usage.context to thesis.id directly, or to thesis.symbol within the thesis’s open window.

See also: Universe, Cohort, Events, and Experiments.

Not financial advice. Cents is an educational and research tool for tracking your own investment theses. Outputs are model-generated and may be inaccurate. You are solely responsible for your own investment decisions.