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.
Philosophy first
Section titled “Philosophy first”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 carriesorchestrator_labelso 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.
Budget mechanics
Section titled “Budget mechanics”Defaults from cents factory init:
| Field | Default | What it means |
|---|---|---|
budget_usd | 100_000 | Max gross notional across all open factory positions |
target_positions | 30 | Derives position_size_usd = budget / target = $3,333 |
entry_threshold | 7.0 | ` |
preemption_margin | 5.0 | New conviction must beat lowest open’s by this much to displace |
cohort_mode | paired | paired opens both legs (2× notional); directional_only opens one |
sizing_mode | equal_dollar | budget / target_positions per leg. Flip to vol_scaled to opt into inverse-vol sizing. |
beta_match_hedge | false | Hedge leg is dollar-matched. Flip to true to opt into 60-day OLS beta-matched sizing. |
default_horizon_days | 30 | Auto-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_run | 5 | Rate-limit so one run can’t reshape the whole book |
max_per_premise_tag | 2 | Concentration 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.
Operating rules
Section titled “Operating rules”Open a thesis when…
Section titled “Open a thesis when…”- Symbol has no current factory thesis (the held-symbols set excludes
both
symbolandhedge_symbol) - Orchestrator’s
|conviction_delta| ≥ entry_threshold - LLM-classified
premise_tagsdon’t push any tag pastmax_per_premise_tagcap - 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_runfor this run - (In paired mode) a sector-ETF hedge is resolvable (SPY fallback when unknown)
Direction follows signal sign
Section titled “Direction follows signal sign”delta > 0→ LONG underlying (+ SHORT hedge in paired mode); target above entry, stop belowdelta < 0→ SHORT underlying (+ LONG hedge in paired mode); target below entry (price has to drop to win), stop above
Close a thesis when…
Section titled “Close a thesis when…”PREMISE_INVALIDATIONalert fires (an ingested event’s tags intersect this thesis’spremise_tags) → outcomeINVALIDATED- Price hits target →
CORRECT - Price hits stop →
INCORRECT - Horizon expires →
UNCLEAR - A higher-conviction candidate preempts it →
PREEMPTED(stratified out of win-rate analytics)
Cooldown
Section titled “Cooldown”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.
What it deliberately doesn’t do
Section titled “What it deliberately doesn’t do”- 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 registertime - 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.
Operational cost (observed)
Section titled “Operational cost (observed)”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.
The implicit deal
Section titled “The implicit deal”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.
Operating the factory
Section titled “Operating the factory”cents factory init # scaffold ~/.cents/factory.tomlcents factory run --dry-run # preview proposed actionscents factory run # LLM arm — the real loopcents factory run --orchestrator random # control arm — uniform-random conviction, no LLM callscents factory run --orchestrator random --orchestrator-seed 42 # reproducible control armcents factory run --max-cost-usd 5.00 # pre-call cost cap; aborts before the offending callcents factory status # current state, last run, budget utilcents factory analyze # cohort-stratified outcomes summarycents factory analyze --by discovery,cohort,regimecents factory analyze --by cohort,orchestrator # the 2x2 LLM-vs-random win-rate tablecents factory analyze --include-cost-per-outcome # adds llm_cost_per_{opened,judged,correct} per cellThe --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.