Strategy primitive
Lucent backtests run on a fixed four-axis substrate: Trigger × Sizing × Entry × Exit. The Trigger picks a date a position opens (e.g., an event matches, a signal crosses a threshold, an alert fires, or two of those compose within a trading-day window). Sizing decides how big that position is. Entry pins when the trade enters the market. Exit pins when the trade closes. This page is the glossary for every arm, parameter, and enum value the Strategy Editor exposes.
For headline cohort numbers (hit rates, capture rates, sample counts), see the Hit Rate methodology. For the broader methodology index, see the methodology hub.
This page is auto-generated from the same description constants the Strategy Editor reads at /admin/backtests/run. A future enum addition surfaces here automatically on the next revalidate.
Trigger arms
The Trigger is the only axis the operator typically customizes deeply. V1.2.1 exposes 5 arms — 3 base arms (event, signal, alert) plus 2 composition arms (positive AND, negative AND-NOT). All field shapes round-trip cleanly through the loader; operand order is loader-canonicalized where applicable so equivalent strategies hash identically.
When an event matchesevent_match
Fires when an event of the named type (and optional subtype) materializes for a watchlisted ticker.
Fires on the trading day an event of the named type materializes for a watchlisted ticker. The optional subtype narrows further (e.g., event_type=sec_filing with subtype=8k_item_2_02 matches only earnings-release 8-Ks). An optional filing-features matcher can require specific atomic features (e.g., guidance_revised=raised). Visibility is gated to events whose createdAt timestamp is at or before the trading day's open — the engine never sees an event before it was ingested.
Fields: Configures eventType, optional eventSubtype, and optional filing-features matchers (the 11 atomic features below).
When a signal crosses a thresholdsignal_threshold
Fires when an entity-level signal score crosses minSignalScore on a watchlisted ticker.
Fires when the named signal type (kol_sentiment or payer_access_commentary) reaches a score of at least minSignalScore on a watchlisted ticker. If mustCrossUpward is true, the prior trading day's score must have been below the threshold (the engine retains a sliding two-day window for cross detection). Signal scores are continuous in [0, 1]; KOL sentiment typically peaks around 0.6–0.8 and payer commentary around 0.5–0.7. Signal types deprecated in Phase 25.7 (competitive_outcome) and never-honest signal types (regulatory_precedent — NULL score on 100% of rows) are excluded from the V1 allowlist.
Fields: Configures signalType, entityType (ticker or substance), minSignalScore (0–1), and mustCrossUpward (require upward crossing).
When an alert firesalert_fires
Fires when an alert of the named type lands in alert_history for a watchlisted ticker.
Fires when an alert_history row of the named alert type lands for a watchlisted ticker. The engine dedups per (ticker, trigger_date, strategy) because alert_history is user-scoped — one underlying event may fan out across many users and produce duplicate alerts. Visibility is gated to alerts whose firedAt timestamp is at or before the trading day's open.
Fields: Configures alertType only — the alert engine carries its own match semantics; this trigger fires whenever a matching alert lands.
Two conditions in a windowwindowed_and
Fires when both child conditions fire on the same ticker within a trading-day window (commutative).
Composes two base trigger arms (event_match or alert_fires; signal_threshold children are deferred to V2 for substrate-bypass safety). Fires on the trading day the second-fire child lands, provided both children fired for the same ticker within windowDays trading days of each other. Operand order is loader-canonicalized so A AND B and B AND A produce the same strategy_hash. Window arithmetic uses NYSE trading days, not calendar days (0 = same day; 5 ≈ 1 calendar week; 21 ≈ 1 calendar month).
Fields: Composes two base triggers (event_match or alert_fires) with a windowDays trading-day window. Commutative — operand order is loader-canonicalized.
One condition but NOT another in a windowwindowed_and_not
Fires when childA fires AND excludedChildB did NOT fire on the same ticker within windowDays.
Negative composition (risk filter / fresh-information selector). Fires when childA fires and no row matching the full excludedChildB spec was observed for the same ticker within the prior windowDays trading days. The trigger date is childA's fire date. The excluded spec is the FULL trigger shape, not just a kind sentinel — excluding clinical_readout is structurally distinct from excluding sec_filing/8-K even if both share a kind. Children are restricted to event_match or alert_fires; the arm is asymmetric and is NOT canonicalized at load time.
Fields: Composes a positive base trigger AND a negative excluded trigger (also event_match or alert_fires). Asymmetric — childA's fire date is the trigger date.
Event types
The 12 event types Lucent surfaces app-wide. The backtest event_match Trigger arm allowlists a narrower subset (8 arms — excludes crl, conference_presentation, patent, and other); the engine rejects the excluded arms at strategy-load time. The descriptions here cover the full app-wide taxonomy.
Clinical Readoutclinical_readout✓ Backtest event_match
Top-line trial results disclosure — primary endpoint, key secondary endpoints, safety summary.
A clinical-trial top-line readout disclosure — primary endpoint, key secondary endpoints, safety summary. Sourced from ClinicalTrials.gov status updates, conference abstracts, and sponsor press releases. The signal is in the magnitude vs prior expectations and the consistency across subgroups; binary 'hit vs miss' framing misses the nuance. Subtypes: results_posted, primary_completion_reached, terminated, withdrawn.
Approvalapproval✓ Backtest event_match
FDA approval action — NDA, BLA, sNDA, sBLA, or label expansion grants market authorization.
An FDA approval action grants market authorization for a new molecular entity (NDA / BLA), a new indication (sNDA / sBLA), or a label expansion. Approval dates are typically discoverable from the FDA's Drugs@FDA database and post-action press releases. The price reaction depends heavily on whether the approval was already priced — the largest moves come from approvals at variance with consensus expectations.
CRLcrl✗ Not in V1 allowlist
Complete Response Letter — FDA's formal rejection citing deficiencies in a submission.
A Complete Response Letter (CRL) is the FDA's formal communication that a submission cannot be approved in its current form, citing specific deficiencies (clinical, manufacturing, label-related). CRLs are not absolute rejections — the sponsor can resubmit after addressing the issues. Magnitude of CRL impact on small-cap names is often 30–80% on the day; large-cap diversified names absorb the hit more quietly.
PDUFApdufa✓ Backtest event_match
Prescription Drug User Fee Act date — FDA's committed action date on a pending NDA/BLA.
The Prescription Drug User Fee Act (PDUFA) date is the FDA's committed action date on a pending New Drug Application (NDA) or Biologics License Application (BLA). It's a date stamp, not an outcome — the action itself can be approval, complete response, or extension. PDUFA dates are typically scheduled 6–10 months out for standard review and 6 months out for priority review.
AdComadcom✓ Backtest event_match
FDA Advisory Committee meeting — outside experts vote on a pending regulatory decision.
An FDA Advisory Committee (AdCom) meeting convenes outside experts (clinicians, statisticians, patient advocates) to vote on a pending regulatory decision. The vote is non-binding but historically influential — the FDA follows the committee recommendation in roughly 70–80% of cases. AdComs are usually scheduled 1–3 months before the related PDUFA date.
CHMP opinionchmp_opinion✓ Backtest event_match
EU regulatory decision from the EMA's Committee for Medicinal Products for Human Use (CHMP).
EU regulatory decisions from the EMA's Committee for Medicinal Products for Human Use (CHMP), sourced from the CHMP monthly meeting-highlights pages. Five subtypes: `chmp_positive_opinion` (new marketing authorization recommendation), `chmp_negative_opinion` (rejection), `chmp_withdrawal` (applicant-initiated withdrawal), `chmp_re_examination` (applicant-requested review of prior negative), and `chmp_extension_of_indication` (label extension for an already-authorized product). CHMP positive opinions typically precede EC marketing authorization by 4–6 weeks and are hard trading catalysts for any monitored substance with EU commercial exposure. INCLUDED in the V1 backtest `event_match` allowlist (Phase 47 § D7 — DIVERGENCE from Phase 45's `safety_signal` exclusion, justified by directional cleanness of CHMP opinion outcomes; forces `BACKTEST_METHODOLOGY_VERSION` 1.3.0 → 1.4.0 rotation). EXCLUDED from the Hit Rate `TIMELY_CAPTURE_EVENT_TYPES` cohort (Phase 47 § D8 — Hit Rate published methodology is US-scoped; widening to EU catalysts would change the published metric's meaning without a corresponding jurisdiction-split render surface).
Abstract Releaseabstract_release✓ Backtest event_match
Conference-abstract publication window — peer-reviewed disclosure of trial data ahead of presentation.
Conference-abstract publication window for major scientific conferences (ASCO, ASH, AACR, ESMO, AHA, ACC, etc.). Abstracts publish 1–4 weeks before the conference proper and often carry the most impactful clinical data of the year. Subtype encoding: <conference>_<year> (e.g., asco_2026). The presentation itself happens at the conference; the abstract release is the substrate-visibility moment.
Conferenceconference_presentation✗ Not in V1 allowlist
Scientific or investor conference presentation — sponsor talks the data at a public venue.
A scientific or investor conference presentation — sponsor talks the data at a public venue (ASCO podium presentation, ESMO oral, R&D day, JPM Healthcare keynote). Subtypes encode the conference (`conference_window` for non-specific windows; `<acronym>_<year>` for specific conferences). NOT on the V1 backtest event_match allowlist — substrate availability is uneven.
Earningsearnings✓ Backtest event_match
Quarterly earnings release — actual vs consensus + forward guidance update.
Quarterly earnings release. The calendar event is the release moment; the directional content lives in the downstream Item 2.02 8-K. Pair with filing-features matchers (earnings_beat_miss, guidance_revised) on event_match strategies for the strongest 'beat-and-raise' or 'miss-and-cut' signals. Consensus estimates source from external analyst-aggregator feeds.
SEC Filingsec_filing✓ Backtest event_match
SEC-filed disclosure (8-K, 6-K, 10-Q, 10-K, 20-F, S-1, etc.) — substrate-visible at filing timestamp.
An SEC-filed disclosure routed through the EDGAR ingestion pipeline — 8-Ks (material events), 6-Ks (FPI material events), 10-Qs / 10-Ks (periodic reports), 20-Fs (FPI annuals), S-1 / S-3 / 424B (offerings). Subtypes include `8k` (un-routed), `8k_item_2_02` (earnings), `8k_item_5_02` (exec/board), `8k_item_7_01` (Reg FD), and the FPI variants. For backtest event_match strategies, pair with filing-features matchers on Item-coded subtypes.
Offeringoffering✓ Backtest event_match
Equity or convertible offering — IPO, follow-on, ATM program, PIPE, shelf takedown.
An equity or convertible offering — IPO, follow-on, at-the-market (ATM) program, PIPE, registered direct, shelf-registration takedown. Typically dilutive in the near term (EPS dilution + overhang); the longer-horizon read depends on what the company plans to do with the proceeds. Subtypes: ipo, shelf_registration, prospectus_supplement, ipo_fpi, shelf_registration_fpi.
Patentpatent✗ Not in V1 allowlist
Patent-related event — issuance, expiration, IPR institution, PTAB ruling, Paragraph IV ANDA filing.
Patent-related events — issuance, expiration (patent cliff), Inter Partes Review (IPR) institution at the PTAB, Paragraph IV ANDA filing, Hatch-Waxman litigation triggers. The signal is typically about franchise revenue durability — losing patent protection on a key drug opens the door to generic entry within 6–18 months. NOT on the V1 backtest event_match allowlist.
Post-market safetysafety_signal✗ Not in V1 allowlist
FDA post-market safety event — AEMS quarterly Potential Safety Signals list entry.
FDA post-market safety events sourced from the AEMS (Adverse Event Monitoring System) quarterly Potential Safety Signals list. V1 ingests one event per (drug, signal, quarter) tuple with subtype `potential_signal`; boxed-warning / REMS / market-withdrawal subtypes ship via a V2 SPEC. Ingested and rendered on entity wikis under a dedicated Post-market safety section, but EXCLUDED from backtest `event_match` triggers (direction is ambiguous — bad for the drug's owner, potentially good for a competitor — see Phase 45 § Non-goals D7) and EXCLUDED from the Hit Rate `TIMELY_CAPTURE_EVENT_TYPES` cohort (Hit Rate measures pre-catalyst awareness; safety signals fire post-market — see Phase 45 § Non-goals D8).
Otherother✗ Not in V1 allowlist
Catch-all event type — events that don't fit the named taxonomy yet.
Catch-all event type for events that don't yet fit the named taxonomy. Most production events route through one of the named arms; `other` accumulates edge cases pending future ontology widening. NOT on the V1 backtest event_match allowlist (an allowlist of `other` would be too coarse to backtest meaningfully).
Backtest support matrix
At-a-glance overview of which event types and subtypes the V1 backtest event_match Trigger arm can filter on. Subtypes inherit allowlist membership from their parent event type — there is no per-subtype gate at the loader. The Strategy Editor's subtype widget surfaces a dropdown for static subtypes and a free-form input for the two dynamic event types.
| Event type | In V1 allowlist | Subtype shape | Known subtypes |
|---|---|---|---|
Clinical Readoutclinical_readout | ✓ | Static | |
Approvalapproval | ✓ | Static | |
CRLcrl | ✗ | None | none |
PDUFApdufa | ✓ | None | none |
AdComadcom | ✓ | None | none |
CHMP opinionchmp_opinion | ✓ | Static | |
Abstract Releaseabstract_release | ✓ | Dynamic (<slug>_<year>) | dynamic only |
Conferenceconference_presentation | ✗ | Static + dynamic | conference_window+ dynamic |
Earningsearnings | ✓ | None | none |
SEC Filingsec_filing | ✓ | Static | |
Offeringoffering | ✓ | Static | |
Patentpatent | ✗ | None | none |
Post-market safetysafety_signal | ✗ | Static | |
Otherother | ✗ | None | none |
Event subtypes
Subtypes narrow an event type to its concrete shape (an sec_filing may be 8k_item_2_02 for earnings, 8k_item_5_02 for exec/board changes, 8k_item_7_01 for Reg FD). The Phase 24z.1 8-K item-code routing is what lets backtest event_match strategies discriminate among the most common 8-K shapes.
Subtypes are grouped under their parent event type below — each subtype's anchor remains event-subtype-<subtype> for the Strategy Editor's "Learn more →" deep links. Some event types use dynamic subtypes shaped as <slug>_<year> (e.g., asco_2026) which aren't enumerated; those fall through to a generic humanizing formatter.
Under Clinical Readoutclinical_readout✓ Backtest event_match
Primary completionprimary_completion_reached✓ Backtest event_match
Trial reached its primary completion date — data analysis is now underway.
Trial reached its primary completion date as reported on ClinicalTrials.gov. Data analysis is now underway; the top-line readout typically follows 4–16 weeks later. The signal is forward-looking: primary completion is the substrate-visible precursor to a readout event.
Results postedresults_posted✓ Backtest event_match
ClinicalTrials.gov results-section update — quantitative top-line numbers posted.
ClinicalTrials.gov results-section update — quantitative top-line numbers posted to the trial record. Often lags the sponsor's press release by weeks-to-months because the CTG results section requires more formal data assembly. Useful as a secondary confirmation signal once a press-release readout has been digested.
Terminatedterminated✓ Backtest event_match
Trial terminated before completion — sponsor halted enrollment or data collection.
Trial was terminated before reaching its planned completion. Reasons vary (futility, safety, business decision, recruitment failure); the ClinicalTrials.gov record carries a brief termination reason. Often a bearish signal but not always — sometimes the sponsor terminated to redirect resources to a more promising program.
Withdrawnwithdrawn✓ Backtest event_match
Trial withdrawn before any subjects enrolled — sponsor cancelled the planned study.
Trial was withdrawn before any subjects enrolled. Less impactful than termination (no patients exposed; no data accumulated) but still a signal that the sponsor changed direction on the planned study. Often coupled with strategic pipeline reprioritization.
Under Approvalapproval✓ Backtest event_match
Tentativetentative✓ Backtest event_match
Tentative FDA approval — application meets standards but is blocked by exclusivity / litigation.
Tentative FDA approval — the application meets all approval standards but cannot be granted final approval because of an unexpired blocking factor (e.g., 30-month stay on a Paragraph IV ANDA challenge, unexpired exclusivity on the reference product, ongoing litigation). The tentative approval converts to final approval once the blocker resolves.
Under CHMP opinionchmp_opinion✓ Backtest event_match
CHMP extension of indicationchmp_extension_of_indication✓ Backtest event_match
CHMP recommended a label extension for an already-authorized product (new indication).
The CHMP recommended a label extension for an already-authorized product — a new indication, expanded patient population, or updated posology on an existing marketing authorization. COVID-19 vaccine strain-update opinions route here as well (mechanically an extension). Directionally clean bullish catalyst for the marketing authorization holder, though typically smaller-magnitude than a new-MA positive opinion because the base commercial franchise already exists.
CHMP negative opinionchmp_negative_opinion✓ Backtest event_match
CHMP recommended against marketing authorization (negative opinion on new MA or extension).
The CHMP recommended against marketing authorization for the product. Directionally clean bearish catalyst. Applies to both new marketing-authorization applications and extension-of-indication applications when the recommendation is negative. Applicants may request re-examination (see the `chmp_re_examination` subtype).
CHMP positive opinionchmp_positive_opinion✓ Backtest event_match
CHMP recommended a new marketing authorization for the product (positive opinion).
The CHMP recommended a new marketing authorization for the product. Directionally clean catalyst for the drug's marketing authorization holder — EC marketing authorization typically follows within 4–6 weeks. Biosimilar / generic positive opinions carry the same subtype (the biosimilar/generic distinction lives in event metadata, not subtype, per SPEC canonical section→subtype mapping — the CHMP opinion mechanic is identical).
CHMP re-examinationchmp_re_examination✓ Backtest event_match
Applicant requested re-examination of a prior CHMP negative opinion.
The applicant requested re-examination of a prior CHMP negative opinion. V1 ingests the re-examination as an event on its own without threading parent-child relationships to the original negative opinion — deferred to V2 if operator demand for outcome-tracking surfaces. The re-examination outcome itself lands as a subsequent event (either `chmp_positive_opinion` reversing the earlier negative, or a confirmed `chmp_negative_opinion`).
CHMP application withdrawnchmp_withdrawal✓ Backtest event_match
Applicant withdrew a pending CHMP application (new MA or extension of indication).
The applicant withdrew a pending CHMP application before a formal opinion was issued. Distinct from an FDA CRL (which is regulator-initiated) — CHMP withdrawal is applicant-initiated. Often signals the sponsor concluded the application was unlikely to receive a positive opinion; the market typically reads this as bearish though less severe than a formal negative opinion. Metadata carries the applicant-initiated flag for future extractor discrimination.
Under Abstract Releaseabstract_release✓ Backtest event_match
Dynamic subtypes shaped as <slug>_<year> are accepted by the loader but not enumerated here. No static subtypes.
Under Conferenceconference_presentation✗ Not in V1 allowlist
Dynamic subtypes shaped as <slug>_<year> are accepted by the loader but not enumerated here. One static fallback exists; see below.
Conference windowconference_window✗ Not in V1 allowlist
Generic conference attendance window — sponsor presented during a conference's date range.
Generic conference attendance window — the sponsor presented during a conference's published date range, but the specific conference identity wasn't captured at ingestion time. For named conferences, the subtype is `<acronym>_<year>` (e.g., asco_2026) rather than this generic catch-all.
Under SEC Filingsec_filing✓ Backtest event_match
20-F (Annual)20f✓ Backtest event_match
Form 20-F — foreign private issuer annual report (the FPI analog of a 10-K).
SEC Form 20-F — annual report filed by Foreign Private Issuers (FPIs). The FPI analog of the 10-K, with audited financials, risk factors, and operating review for the fiscal year. Filed within 4 months of fiscal-year end. Phase 24w added FPI labeling support.
6-K6k✓ Backtest event_match
Form 6-K — foreign private issuer current report (the FPI analog of an 8-K).
SEC Form 6-K — current report filed by Foreign Private Issuers (FPIs). Functionally the FPI analog of the 8-K, but the content shape varies more (foreign companies often attach their home-country press release and regulatory filings rather than a US-shaped Item-coded body). Phase 24w added FPI labeling support.
8-K8k✓ Backtest event_match
Form 8-K — current report disclosing material events not covered by periodic filings.
SEC Form 8-K — the current report a domestic registrant files for material events between quarterly / annual reports. Items 1.01 (material agreements), 2.02 (earnings), 5.02 (exec/board), 7.01 (Reg FD), 8.01 (other) are the most common. Bare `8k` subtype indicates the filing wasn't successfully routed to an Item-code-specific subtype by Phase 24z.1's `routeEightKByItems`.
8-K (Material Definitive Agreement)8k_item_1_01✓ Backtest event_match
8-K filed under Item 1.01 — material definitive agreement (collaboration, license, M&A).
8-K filed under Item 1.01 (entry into a material definitive agreement). Covers collaboration / license / development / co-promotion agreements (partner relations) AND the announcement leg of M&A transactions (acquirer_of relations). Phase 24b's counterparty extraction (Ex-99.1 press release or cover-page fallback) seeds the `partner` and `acquirer_of` knowledge-graph relations. Strategy authors can pair with `partnership_announced` / `acquisition_announced` filing-features matchers on backtest event_match strategies. Routed by Phase 24b's extension of `routeEightKByItems` (precedence: 2.01 > 1.01 > 2.02 > 5.02 > 7.01).
8-K (Completion of Acquisition)8k_item_2_01✓ Backtest event_match
8-K filed under Item 2.01 — completion of M&A transaction; closing-day disclosure.
8-K filed under Item 2.01 (completion of acquisition or disposition of assets). Closing-day disclosure for M&A transactions, asset sales, rights/license transfers, and divestitures. Phase 24b's counterparty extraction targets `acquisitionScope='whole_company'` filings only (asset sales / rights deals / divestitures do not seed `acquirer_of` relations); the closing-day Item 2.01 row also deactivates the corresponding `partner` row from the earlier Item 1.01 announcement. Pair with the `acquisition_announced` filing-features matcher on backtest event_match strategies. Routed by Phase 24b's extension of `routeEightKByItems` (highest precedence — wins over 1.01 + all later Items).
8-K (Earnings)8k_item_2_02✓ Backtest event_match
8-K filed under Item 2.02 — quarterly earnings release with actual numbers + guidance.
8-K filed under Item 2.02 (results of operations and financial condition). Carries the quarterly earnings release with actual revenue/EPS, consensus comparison, and forward guidance. Pair with the `earnings_beat_miss` and `guidance_revised` filing-features matchers for backtest event_match strategies. Routed by Phase 24z.1 `routeEightKByItems`.
8-K (Exec/Board)8k_item_5_02✓ Backtest event_match
8-K filed under Item 5.02 — officer/director appointment, departure, compensation changes.
8-K filed under Item 5.02 (departure of directors or certain officers; election of directors; appointment of certain officers; compensatory arrangements). Both officer and director changes route here. Pair with `executive_departure`, `executive_appointment`, `board_change` filing-features for backtest strategies; the dual-hat rule (one person changes both an officer AND a board role) fires multiple features. Routed by Phase 24z.1.
8-K (Reg FD)8k_item_7_01✓ Backtest event_match
8-K filed under Item 7.01 — Regulation FD disclosure, typically conference deck or guidance update.
8-K filed under Item 7.01 (Regulation Fair Disclosure). Sponsor is making a material disclosure to a non-public audience (analyst day deck, conference presentation slides, mid-quarter guidance update) and is fulfilling the parallel public-disclosure requirement. Often contains forward-looking content. Routed by Phase 24z.1.
Under Offeringoffering✓ Backtest event_match
IPOipo✓ Backtest event_match
Initial Public Offering — company's first sale of stock to the public.
Initial Public Offering — the company's first sale of stock to the public via a registration-statement effectiveness. Substrate channel is the SEC's S-1 form (routed through EDGAR ingestion), but the canonical event type is `offering` (NOT `sec_filing`). Pricing typically lands the night before the first trading day; the open is the substrate-visible moment for backtest purposes. Often associated with high short-term volatility as price discovery occurs.
IPO (FPI)ipo_fpi✓ Backtest event_match
Foreign private issuer IPO — first US public listing by a non-US-domiciled company.
Initial Public Offering by a Foreign Private Issuer (FPI) — first US public listing by a non-US-domiciled company. Often dual-listed with the company's home-country exchange. Phase 24w added FPI offering labeling support.
Prospectus supplementprospectus_supplement✓ Backtest event_match
424B prospectus supplement — actual takedown of securities from a previously-filed shelf.
Form 424B prospectus supplement — the actual takedown of securities from a previously-filed S-3 shelf registration. This IS the dilution event (vs the shelf itself, which only authorizes). Often filed concurrently with an offering pricing announcement.
Shelf registrationshelf_registration✓ Backtest event_match
S-3 shelf registration — pre-authorization to issue securities up to a stated dollar amount over 3 years.
Form S-3 shelf registration — pre-authorization from the SEC to issue securities (equity, debt, warrants) up to a stated dollar amount over the next 3 years. Filing the shelf itself isn't dilutive (no securities are actually issued); takedowns from the shelf via prospectus supplements (424B5) are the actual dilution events.
Shelf (FPI)shelf_registration_fpi✓ Backtest event_match
Foreign private issuer shelf registration — FPI analog of the S-3 shelf.
Foreign Private Issuer shelf registration — the FPI analog of an S-3 shelf. Pre-authorizes future securities issuance over a 3-year window without committing to specific timing or size. Phase 24w added FPI labeling.
Under Post-market safetysafety_signal✗ Not in V1 allowlist
Potential safety signalpotential_signal✗ Not in V1 allowlist
AEMS quarterly Potential Signals list entry — FDA editorial flag on an approved drug.
FDA AEMS (Adverse Event Monitoring System) quarterly Potential Signals of Serious Risks / New Safety Information list entry. Deterministic parse from the FDA-published quarterly HTML table; one events row per (drug, signal, quarter) tuple. Drug-name column matched against `substances.aliasMap`: exact-single-match populates `event.ticker_id` from the substance's linked ticker; class-shorthand or multi-substance matches land with `ticker_id=NULL` and one `event_substances` row per matched substance. V1 does NOT ingest boxed warnings / REMS modifications / market withdrawals directly (those show up in the AEMS list one quarter later; V2 SPEC closes the intra-quarter gap).
Signal types
The full knowledge-layer signal taxonomy (11 arms). The backtest signal_threshold Trigger arm allowlists only 2 (kol_sentiment, payer_access_commentary) — the rest are either deprecated for backtest use or surface only in the wiki / EDA layers. Deprecated arms carry a badge and name the deprecation phase in their long text.
KOL sentimentkol_sentiment
Aggregated sentiment from key-opinion-leader posts and statements about the entity.
Aggregates sentiment from KOL (key-opinion-leader) posts on social media, conference commentary, and analyst-channel chatter about the entity. Scores are continuous in [0, 1]; typical peaks are 0.6–0.8 around well-received readouts. One of the 2 V1-allowlisted signal types for the backtest engine's signal_threshold Trigger arm.
Payer-access commentarypayer_access_commentary
Sentiment from payer-side commentary on coverage, formularies, and prior-auth burden.
Aggregates sentiment from payer-side commentary (PBMs, plan medical directors, formulary committees) about coverage decisions, prior-authorization burden, and reimbursement posture toward the entity. One of the 2 V1-allowlisted signal types for the backtest engine's signal_threshold Trigger arm. Lower-frequency but stronger-directional than KOL sentiment when it fires.
Enrollment paceenrollment_pace
Rate at which a clinical trial is enrolling subjects relative to its enrollment plan.
Captures the rate at which an active clinical trial is enrolling subjects relative to its disclosed enrollment plan. Slow pace can signal site activation issues, eligibility-criteria over-restriction, or competitive enrollment pressure from rival trials. Fast pace can signal strong investigator interest or favorable indication tailwinds. Score derived from trial-status disclosures and earnings-call commentary.
Short-interest trendshort_interest_trend
Direction and magnitude of recent short-interest changes on the ticker.
Captures the direction and magnitude of recent short-interest changes on the ticker. Rising short interest can signal bearish positioning (or hedging against an upcoming catalyst); falling short interest can signal a short squeeze or covering wave. Score blends absolute level with rate-of-change. Surfaces in the wiki signals block and EDA dashboards; not on the V1 backtest allowlist.
Insider activityinsider_activity
Net direction of recent Form 4 / Form 5 insider transactions on the ticker.
Captures the net direction of recent Form 4 (officer/director buys-sells) and Form 5 (annual insider position) transactions on the ticker. Insider buying is generally interpreted as a bullish signal (insiders have non-public visibility); insider selling is more ambiguous (could be planned 10b5-1 sales, tax-driven, or genuinely bearish). Surfaces in the wiki signals block; not on the V1 backtest allowlist.
Patent riskpatent_risk
Patent-cliff and IP-challenge exposure on a key revenue-generating asset.
Captures patent-cliff timing and IP-challenge exposure on key revenue-generating assets — looming generic entry, ongoing Paragraph IV ANDA challenges, IPR proceedings at the PTAB. Higher scores indicate higher near-term risk to franchise revenues. Surfaces in the wiki signals block and substance-detail pages; not on the V1 backtest allowlist.
Pipeline combination disclosurepipeline_combination_disclosure
Combination-therapy disclosures from earnings transcripts (Phase 22c).
Captures disclosures about combination-therapy development from earnings-call transcripts (Phase 22c). Combination strategies often signal a pivot from monotherapy ceiling toward expansion-cohort opportunities; the transcript context matters more than the headline. Surfaces in the wiki signals block; not on the V1 backtest allowlist.
Regulatory strategy statementregulatory_strategy_statement
Regulatory-pathway intent disclosures (BLA filing plans, accelerated-approval pursuit) from transcripts.
Captures management's regulatory-pathway intent disclosures from earnings transcripts (Phase 22c) — BLA filing timelines, accelerated-approval pursuit, breakthrough-designation strategy. The forward-looking nature of the signal makes it especially valuable around inflection points. Surfaces in the wiki signals block; not on the V1 backtest allowlist.
Commercial execution guidancecommercial_execution_guidance
Commercial-trajectory disclosures (launch trajectory, payer ramp, prescriber adoption) from transcripts.
Captures commercial-trajectory disclosures from earnings transcripts (Phase 22c) — launch ramp trajectory, payer-coverage progress, prescriber-adoption metrics, sales-force expansion plans. The transcript context typically carries more nuance than the press-release summary; especially valuable for post-launch ramp tracking. Surfaces in the wiki signals block; not on the V1 backtest allowlist.
Competitive outcomecompetitive_outcomeDeprecated
DEPRECATED Phase 25.7. Captured peer-trial outcomes; substrate gaps made the scorer's output unusable.
Originally captured peer-trial outcomes (e.g., a competitor's Phase 3 readout signaling read-through to the watchlisted ticker). DEPRECATED in Phase 25.7 (2026-06-15) after the Task 0 probe identified three structural substrate gaps: (1) the scorer checked event_type='fda_approval' but the substrate stores 'approval' (the FDA-approval branch never fired); (2) events.outcome is a 2-value status flag, not the free-text the keyword set assumed (outcome-based keyword matching was structurally impossible); (3) peer relationships are company-level, not therapy-area, so a Pfizer clindamycin event fired as Merck competitive pressure. Excluded from the V1 backtest signal_threshold allowlist; the engine rejects this signalType at strategy-load time. New strategies should use kol_sentiment or payer_access_commentary instead.
Regulatory precedentregulatory_precedentDeprecated
DEPRECATED. Second failed substrate probe (Phase 25.22 Task 0a) — outcome field is a 2-value status flag.
Captures regulatory-precedent context (prior FDA decisions on similar molecules/indications) as qualitative wiki content. DEPRECATED for backtest use: TWO sequential substrate probes have failed. Phase 25.7 originally documented the substrate gap with competitive_outcome (sibling signal type sharing the same approval-event substrate); Phase 25.22 Task 0a re-probed regulatory_precedent specifically after fixing the Phase 17 event-type string mismatch (["fda_approval", "fda_rejection", "pdufa"] → ["approval", "pdufa"]) — and discovered a SECOND stacked substrate gap underneath the string-mismatch one. events.outcome is a 2-value status flag (100% 'approved' on event_type='approval' rows, 100% NULL on event_type='pdufa' rows), not the free-text the scorer assumed. The scorer's outcome.includes("approv") substring match therefore returns true for every surviving substrate row, so every focal ticker's approvalRate computes to 1.0 — no cross-ticker variance, no upward crossings, no honest threshold signal. The Task 0a probe verdict: Gate B FAIL (1 distinct signalValue across the probed universe), Gate C FAIL (per-ticker approvalRate stddev = 0.0000), Gate D FAIL (0 upward crossings of 0.5 over 90d on the curated 30-ticker universe). Future fix paths require substantive substrate work, not extractor surgery: (1) populate events.outcome with substantive strings that distinguish CRL / RTF / accelerated-approval / full-approval shapes; (2) rebuild the scorer on a CRL/RTF-distinguishable substrate; (3) retire regulatory_precedent permanently alongside competitive_outcome. Excluded from the V1 backtest signal_threshold allowlist; the engine rejects this signalType at strategy-load time. The wiki layer continues to surface this signal as narrative context.
Alert types
The 8 V1 alert types the alert_fires Trigger arm can subscribe to. One underlying event can fan out across many users' alert rows; the backtest engine dedups per (ticker, trigger_date, strategy) before materializing trades. Phase 61 (2026-08-19) adds a 9th app-only arm market_anomaly for the computed anomaly detector — deliberately NOT admitted to the backtest alert_fires vocabulary (SPEC § D9).
Read-through alertread_through
Fires when an event on one ticker plausibly read-throughs to a related ticker on the user's watchlist.
Read-through alerts identify cross-ticker implications — e.g., a positive Phase 3 readout on competitor X may carry a bearish read-through to ticker Y when both share an indication or mechanism. Fired by the alert engine when a watchlisted ticker's peer set sees a high-impact event. One underlying event fans out across many users' alert rows; the backtest engine dedups per (ticker, trigger_date, strategy) before materializing trades.
Any catalyst eventevent_any
Catch-all alert that fires when any catalyst event lands on a watchlisted ticker.
Fires for every catalyst event on a watchlisted ticker regardless of type — the broadest alert subscription. Useful as a backtest baseline against the per-event-type alerts (event_pdufa, event_earnings, etc.) to measure marginal benefit from event-type-specific subscriptions.
PDUFA event alertevent_pdufa
Fires when a PDUFA date (FDA action date for a pending NDA/BLA) lands or moves.
Fires when a PDUFA date for a watchlisted ticker is added, advanced, delayed, or reached. PDUFA dates are FDA-action commitments on pending NDA/BLA submissions — the signal is the date itself, not the outcome. Outcome semantics are carried separately on the approval / crl event types.
Earnings event alertevent_earnings
Fires when an earnings release event lands on a watchlisted ticker.
Fires when an earnings release lands on a watchlisted ticker. The earnings event itself is calendar-stamped; downstream 8-K item 2.02 filings carry the directional content (beat/miss/inline + guidance revision). Pair with filing-features matchers on event_match for guidance-aware strategies.
AdCom event alertevent_adcom
Fires when an FDA Advisory Committee meeting lands on a watchlisted ticker.
Fires when an FDA Advisory Committee (AdCom) meeting is scheduled, advanced, delayed, or held for a watchlisted ticker. AdCom recommendations are non-binding but historically high-information events; the FDA typically follows the committee vote in 70–80% of cases.
Post-market safety alertevent_safety_signal
Fires when the FDA lists a new post-market safety signal against a drug on a watchlisted ticker (FAERS AEMS quarterly list).
Fires when the FDA's Adverse Event Monitoring System (AEMS) quarterly Potential Safety Signals list adds a new signal against a drug tied to a watchlisted ticker. LOAD-BEARING V1 discipline (Phase 45 § D6): the evaluator fires this alert ONLY when the safety_signal event carries a non-null ticker_id — a single-drug AEMS entry with an unambiguous substance→ticker match. Class-shorthand AEMS entries that name a drug class (e.g., 'GLP-1 receptor agonists') land in the events substrate with ticker_id=NULL and fan out to each matched substance's wiki, but they do NOT fire this alert type V1. Rationale: the alerts.ticker_symbol substrate is ticker-scoped, not substance-scoped — there is no user-facing 'watchlist by substance' V1 that could fire on event_substances membership. Substance-scoped alerts + cross-entity read-through on safety signals are V2 work. Backtest surface: available in the alert_fires Trigger arm; the parent safety_signal event_type is EXCLUDED from the event_match Trigger arm (see Phase 45 § Non-goals D7) because directional return semantics on post-market safety are ambiguous (bad for the drug's owner, potentially good for a direct competitor).
CHMP opinion alertevent_chmp_opinion
Fires when a CHMP opinion (positive/negative/withdrawal/re-examination/extension) lands on a watchlisted ticker.
Fires when the EMA's Committee for Medicinal Products for Human Use (CHMP) publishes an opinion — positive opinion (recommendation for marketing authorization), negative opinion (rejection), applicant withdrawal, re-examination, or extension of indication — for a drug tied to a watchlisted ticker. LOAD-BEARING V1 discipline (Phase 47 § D6 parity with Phase 45): the evaluator fires this alert ONLY when the chmp_opinion event carries a non-null ticker_id. Class-shorthand CHMP entries (multi-substance fan-out via event_substances) are ingested + rendered on wikis but do NOT fire alerts V1 — substance-scoped alert substrate is V2. Backtest surface: BOTH the alert_fires arm here AND the parent chmp_opinion event_type in the event_match Trigger arm are admitted (Phase 47 § D7 — DIVERGENCE from Phase 45's safety_signal exclusion posture, justified by directional cleanness of CHMP opinion outcomes; forces BACKTEST_METHODOLOGY_VERSION 1.3.0 → 1.4.0 rotation).
Signal spike alertsignal_spike
Fires when an entity-level signal score jumps materially day-over-day.
Fires when an entity_signals.signal_score for a watchlisted ticker jumps materially relative to its prior value. Spike-detection is engine-internal; the alert itself carries no threshold semantics — a strategy that wants explicit threshold control should use the signal_threshold Trigger arm directly.
Catalyst remindercatalyst_reminder
Time-based reminder fired ahead of a known upcoming catalyst on a watchlisted ticker.
User-scheduled reminder fired ahead of a known upcoming catalyst (PDUFA approaching, earnings next week, AdCom this month). Reminders are user-configured so the alert_history row carries the user's intent, not the engine's. Less common as a backtest trigger than the event_* alerts.
Unusual Move alertmarket_anomaly
Fires when a watchlisted ticker moves further than its own recent volatility explains, gated to filter macro-day biotech-wide moves.
Fires when the computed anomaly detector (Phase 61 § D5) flags a watchlisted ticker whose intraday move is larger than its own trailing-30d volatility explains — a signal that something ticker-specific is happening even before the underlying catalyst has been extracted into the events substrate. Three independent detector arms: premarket_gap (fires when the pre-market price deviates from the most recent COMPLETED regular session's close by > max(5%, 3σ × prior-30d daily-return σ)); rth_move (fires when regularMarketPrice deviates from regularMarketOpen by > max(5%, 3σ × prior-30d open-to-close σ)); and unusual_volume (fires when full-day-projected volume exceeds 4σ × trailing-30d daily-volume σ). All arms are 4-dimension-gated (absolute floor + ticker-σ + liquidity floor at $5M avg-daily-dollar-volume + sector-macro filter that suppresses fires when XBI moves > 2% and the ticker's excess-move-vs-XBI is < 3%). Alert firing is INDEPENDENT of the events substrate — the market_anomalies table is separate from events (SPEC § D2), and this alert type is deliberately absent from the backtest `alert_fires` Trigger vocabulary (SPEC § D9). Widening the backtest vocabulary would require methodology-hash rotation + Phase 25.5 burn-in clock reset. Alerts are OPT-IN V1: no auto-enrollment for watchlisted tickers.
Filing atomic features
11 atomic features extracted from routed 8-K filings. Used as optional matchers on the event_match Trigger arm (e.g., event_type=sec_filing AND guidance_revised=raised). 7 are booleans; 4 are polarity enums (guidance_revised, earnings_beat_miss, regulatory_update, litigation_event) — enum-shaped features show their per-value sub-tables below. Matching null is not supported; strategies compose positively (require a specific arm) so the absent case is never directly matchable.
Earnings beat/missearnings_beat_miss
Enum: beat | miss | inline. Captures actual-vs-consensus earnings outcome from Item 2.02 releases.
Captures the actual-vs-consensus earnings outcome from Item 2.02 earnings releases. Enum values: beat, miss, inline. Null when the filing isn't an earnings release. Pair with guidance_revised in a windowed_and trigger to capture 'beat AND guidance raised' or the bearish inverse — the two-axis story is often more informative than either axis alone.
Enum values
Earnings beat
beatReported earnings exceeded the consensus estimate (revenue and/or EPS).
Reported earnings exceeded the consensus analyst estimate on revenue and/or EPS. The magnitude matters — a $0.01 beat vs a $0.10 beat carry very different signal strength. Pair with guidance_revised=raised for the strongest beat-and-raise shape.
Earnings miss
missReported earnings fell short of the consensus estimate (revenue and/or EPS).
Reported earnings fell short of the consensus estimate. The magnitude matters; a near-miss with guidance reaffirmed reads differently than a deep miss with guidance withdrawn. Pair with guidance_revised=lowered for the strongest miss-and-cut shape.
Earnings in-line
inlineReported earnings matched the consensus estimate within typical tolerance.
Reported earnings matched the consensus estimate within the typical 1–2% tolerance band. Often a neutral signal — the company met its expected number without surprising in either direction. Forward guidance is usually the swing factor in in-line earnings releases.
Guidance revisedguidance_revised
Enum: raised | lowered | reaffirmed | withdrawn. Captures forward-guidance changes in earnings releases.
Captures forward-guidance changes typically disclosed in Item 2.02 earnings releases. Enum values: raised, lowered, reaffirmed, withdrawn. Null when the filing contains no guidance content. Matching strategies should pick the directional arm explicitly (e.g., guidance_revised=raised); reaffirmed is a neutral signal but still distinct from null (the company actively chose to reaffirm rather than stay silent).
Enum values
Guidance raised
raisedCompany increased its forward financial guidance (revenue, EPS, or operating metric).
Company increased its forward guidance on revenue, EPS, or another operating metric. Often a bullish signal — the company has internal visibility into outperformance vs prior guidance. Pair with earnings_beat_miss=beat for the strongest 'beat-and-raise' shape.
Guidance lowered
loweredCompany decreased its forward financial guidance (revenue, EPS, or operating metric).
Company decreased its forward guidance. Often a bearish signal — the company is internally signaling underperformance vs prior guidance, sometimes coupled with a near-term commercial headwind or trial setback. Pair with earnings_beat_miss=miss for the strongest 'miss-and-cut' shape.
Guidance reaffirmed
reaffirmedCompany explicitly reaffirmed its existing forward guidance with no change.
Company explicitly reaffirmed its existing forward guidance without raising or lowering. Often a neutral-to-mildly-positive signal — the company actively chose to communicate stability rather than stay silent, which carries some information value. Distinct from null (no guidance content in the filing at all).
Guidance withdrawn
withdrawnCompany withdrew its prior forward guidance entirely (no replacement issued).
Company withdrew its prior forward guidance without issuing a replacement. Often a bearish signal — the company has lost confidence in its prior projection but isn't yet ready to commit to a new one. Common during macro shocks, regulatory uncertainty, or post-acquisition integration periods.
Partnership announcedpartnership_announced
Boolean. Fires on licensing, JV, collaboration, and co-development announcements (typically Items 1.01 / 8.01).
Boolean feature that fires when the filing announces a new licensing deal, joint venture, collaboration, or co-development arrangement. Typically appears in Items 1.01 (material definitive agreement) or 8.01 (other events). Structurally distinct from acquisition_announced — license-plus-equity-stake deals can fire both. False does not mean 'no strategic activity'; it means the routed 8-K didn't describe one.
Partnership terminatedpartnership_terminated
Boolean. Fires when a previously-disclosed partnership is terminated, opted out, or expired.
Boolean feature that fires when a previously-disclosed partnership, license, or collaboration is terminated, opted out, or formally allowed to expire. Often a bearish signal but not always (sometimes the terminated deal frees the company to pursue a higher-value path; PR-review the context). Typically appears in Item 1.02 (termination of material agreement).
Acquisition announcedacquisition_announced
Boolean. Fires on M&A announcements (the company as either acquirer or target).
Boolean feature that fires when the filing announces a merger or acquisition — the filing company is either the acquirer or the target. Kept structurally distinct from partnership_announced per Probe D 2026-06-22: LLY's Q1 2026 M&A and ABBV's $10.9B Apogee deal don't fit a partnership shape cleanly, and the signaling profile (full M&A vs licensing/JV) differs materially. Typically appears in Items 1.01 / 2.01 / 8.01.
Dilution announceddilution_announced
Boolean. Fires on follow-on offerings, ATM programs, and other share-count dilutive events.
Boolean feature that fires when the filing announces a follow-on offering, at-the-market (ATM) program, PIPE, registered direct, or other event materially increasing share count. Typically appears in Items 1.01 / 8.01 / 3.02 or on separate S-1/S-3/424B filings. Often a near-term bearish signal due to mechanical EPS dilution and overhang; pair with the company's cash runway context for the longer-horizon read.
Executive departureexecutive_departure
Boolean. Fires on officer-level departures (CEO/CFO/COO/CMO retirement or resignation).
Boolean feature that fires when an executive officer departs (retires, resigns, or is dismissed). Typically appears in Item 5.02. Dual-hat rule: when one person holds both an officer role AND a board role and the filing changes both capacities, this feature fires alongside board_change. A standalone CEO/CFO/COO/CMO retirement fires this feature; a director-only departure does NOT (use board_change for that).
Executive appointmentexecutive_appointment
Boolean. Fires on new officer-level appointments (CEO/CFO/COO/CMO hire or promotion).
Boolean feature that fires when a new executive officer is appointed (external hire or internal promotion). Typically appears in Item 5.02. Dual-hat rule: when one person is appointed to both an officer role AND a board role, this feature fires alongside board_change. A new independent director appointment (no officer capacity) does NOT fire this feature; use board_change.
Board changeboard_change
Boolean. Fires on board-of-directors changes (director appointment, departure, resignation).
Boolean feature that fires when the board of directors changes — director appointment, departure, resignation, or replacement. Typically appears in Item 5.02 (which covers both officer and director changes). Independent of executive_departure / executive_appointment under the dual-hat rule: a person changing both an officer role AND a board role fires both features; a pure-director change fires only this one.
Regulatory updateregulatory_update
Enum (polarity): positive | negative | neutral. Captures FDA / regulatory-body action posture.
Polarity enum (NOT a boolean) capturing FDA or other regulatory-body action posture. Enum values: positive (approval, breakthrough designation, supportive AdCom vote), negative (CRL, Refusal-to-File, Complete Response Letter, negative AdCom), neutral (status update, label change with no clear directional implication). Typically appears in Items 7.01 / 8.01. **Polarity-enum-vs-boolean discipline (Phase 24z.1 D5):** encoding 'regulatory_update_present: true' would collapse FDA approval and Refusal-to-File into the same matcher substrate — the Phase 25.7 competitive_outcome failure mode. Strategies match on one of the three concrete arms; matching on null is not supported because strategies compose positively (require positive / negative / neutral, never 'match the absent case').
Enum values
Positive regulatory update
positiveApproval, breakthrough designation, supportive AdCom vote, or other favorable regulatory action.
Favorable FDA or other regulatory-body action — approvals, breakthrough designations, supportive AdCom votes, fast-track designations, accelerated-approval grants. The strongest version of the signal; downstream price reaction is typically positive on small-cap names and muted on large-cap names where the outcome was already priced.
Negative regulatory update
negativeCRL, Refusal-to-File, negative AdCom vote, or other unfavorable regulatory action.
Unfavorable FDA or other regulatory-body action — Complete Response Letter (CRL), Refusal-to-File, negative AdCom vote, clinical hold, BLA withdrawal. The strongest bearish version; small-cap names can drop 30–80% on a CRL while large-cap diversified names absorb the hit more quietly.
Neutral regulatory update
neutralRegulatory status update, label change, or other action with no clear directional implication.
Regulatory action with no clear directional implication — routine label updates, post-marketing commitment status reports, ongoing-review acknowledgments. Distinct from null (no regulatory content at all): the filing actively disclosed a regulatory development, but the polarity is ambiguous or context-dependent.
Litigation eventlitigation_event
Enum (polarity): filed | settled | judgment. Captures litigation lifecycle stage and directional posture.
Polarity enum (NOT a boolean) capturing litigation lifecycle stage. Enum values: filed (new lawsuit against the company), settled (resolution by mutual agreement; often bearish on size but resolves overhang), judgment (court ruling — direction depends on context). Typically appears in Item 8.01. **Polarity-enum-vs-boolean discipline (Phase 24z.1 D5):** a single material_litigation: true boolean would collapse LLY's litigation-reserve charge, MRNA's $0.9B Arbutus settlement, and a fresh lawsuit into the same matcher even though all three signal in different directions. The enum split keeps the directional contract explicit. Strategies match on one of the three concrete arms; matching on null is not supported because strategies compose positively (the absent case is not directly matchable).
Enum values
Litigation filed
filedNew lawsuit filed against the company (the filing company is the defendant).
New lawsuit filed against the company — the company is typically the defendant. Often a near-term bearish signal due to overhang and potential reserve charges, but the actual size and likelihood of liability matters more than the headline. Investor reaction usually fades within 1–2 weeks unless the suit is high-impact (e.g., DOJ enforcement, large class action).
Litigation settled
settledPreviously-disclosed lawsuit resolved by settlement (typically resolves an overhang).
Previously-disclosed lawsuit resolved by mutual-agreement settlement. Often bearish on size (the company is paying out) but bullish on overhang removal — the uncertainty around 'what could this cost us?' is replaced by a concrete number. Net signaling depends on whether the settlement is materially better or worse than what the market had priced.
Litigation judgment
judgmentCourt ruling on a lawsuit (direction depends on whether the company won or lost).
Court ruling on a previously-disclosed lawsuit. Direction depends on the outcome: a favorable judgment is bullish (uncertainty resolved + no payout); an adverse judgment is bearish (payout + potential appeals). Often a high-information-value signal because court rulings carry more finality than settlements (less room for renegotiation).
Sizing arms
V1.3.0 ships two Sizing arms: fixed_notional (capital-decoupled — perTrade is an absolute dollar number) and equal_weight (capital-coupled — perTrade = weightFraction × resolvedStartingCapital, resolved at entry). Phase 25.22 § D2 discloses the equal_weight semantic: two runs of the same strategy at different startingCapital produce the SAME strategy_hash but materially different equity curves. signal_weighted sizing remains V2-deferred (see Out-of-scope V1 below).
Fixed dollar amount per tradefixed_notional
Every trade gets the same dollar amount (perTrade). The engine sizes a hypothetical capital pool to fit; positions never block each other.
Every trade is allocated the same fixed dollar amount specified by perTrade. Under methodology v1.1.0+ the engine resolves a hypothetical capital pool ($C) — either auto-computed to admit the peak concurrent position count at ~30% peak deployment, or operator-pinned via the startingCapital config — and marks open positions to market against it. No compounding, no leverage, no displacement: a new trigger never waits for a cash reserve and never bumps an existing position; $C is sized to fit them. The capital-free CTP cumulative/annualized return is the honest cross-strategy comparison (it strips $C entirely); the capital-bound equity-curve metrics (cumulative return, max drawdown) answer 'what would a $C account have done' and fire a CAPACITY-BOUND banner when an operator-pinned $C produces > 60% peak deployment. The sibling Phase 25.22 v1.3.0 equal_weight arm couples per-trade dollar amount to $C via a weightFraction; signal_weighted sizing stays V2-deferred until rebalancing cadence and cash accounting are pinned.
Equal weight per positionequal_weight
Position size = weightFraction × resolvedStartingCapital. Capacity-coupled per Phase 25.22 § D2.
Phase 25.22 v1.3.0 capacity-coupled sizing arm. At entry the engine resolves perTrade = weightFraction × resolvedStartingCapital and persists that concrete dollar number on the Trade record (same downstream shape as fixed_notional from the trade-row perspective). The load-bearing departure from fixed_notional: per Phase 25.22 SPEC § D2, equal_weight binds the entry size to the runtime $C, so two runs of the SAME strategy at different startingCapital produce identical strategy_hash but materially different equity curves — the strategy hash covers Strategy shape only, and runtime $C lives on BacktestRunContext (not a hash input). Methodology body discloses this divergence honestly; the Phase 25.19 sweep substrate exposes it as cells that share a hash but produce different output when a sweep varies startingCapital against an equal_weight strategy. Default $C resolution for fixtures that don't pin a startingCapital uses the auto-$1M default (per Phase 25.13 Phase B formula floor), so a weightFraction=0.05 strategy with no pinned $C sizes positions at $50K. weightFraction is loader-validated as finite, > 0, ≤ 1; the methodology body flags weightFraction > 0.5 as substrate-thin (only 1-2 positions ever open before deployment saturation) without rejecting.
Capital accounting (v1.1.0+)
Under methodology v1.1.0 (Phase 25.13, shipped 2026-06-22) every backtest runs against a hypothetical capital pool — fixed-notional per trade, no leverage, no compounding. Daily equity value V_t = cash + Σ open-position MTM; cumulative return V_T / V_0 − 1; max drawdown is peak-to-trough on V_t. The pool's size and behavior are config-level concerns living on BacktestConfig, NOT on Strategy — two runs of the same strategy at different starting capital produce the same strategy_hash but different equity curves. The numbers visible on the backtest detail page (starting_capital, starting_capital_source, peak_deployment_fraction) are all sourced from this layer.
Starting capital ($C)startingCapital
The size of the hypothetical capital pool against which positions are marked to market.
Lives on BacktestConfig, NOT on Strategy. Two runs of the same strategy at different startingCapital are honestly "same strategy under different capacity assumptions" — same strategy_hash, different equity curves. The engine resolves $C in one of two modes: auto (the default — sized to fit the strategy's peak concurrent position count at ~30% peak deployment) or pinned (operator overrides via --starting-capital on the CLI, or the Strategy Editor's capital input). Cron-scheduled runs always use auto-resolution so weekly numbers stay cross-strategy comparable.
Auto-resolved $Cstarting_capital_source=auto
Sized to admit the strategy's peak concurrent position count at ~30% deployment.
Three-phase resolution under Sizing.kind === "fixed_notional". Phase A materializes the trade list and computes peak concurrent occupancy by date arithmetic only (independent of $C — entry/exit dates from the Trigger × Exit definition). Phase B sets $C = ceil(peakConcurrent × perTrade / 0.3), targeting ~30% peak deployment. Phase C walks trading days building the equity curve. Auto-resolved runs never fire the CAPACITY-BOUND banner by construction. Example: a strategy whose peak concurrent count is 78 at $10,000/trade resolves to $C ≈ $2,600,000 — biotech catalyst clustering reality (the 2026-03-23 cluster Phase 25.9 documented, 24 of 30 curated-universe tickers all filing 8-Ks on one trading day), not a methodology bug.
Under Sizing.kind === "equal_weight" (Phase 25.22 v1.3.0) the peak-based Phase B formula is circular — perTrade itself depends on $C via perTrade = floor(weightFraction × $C), so the engine cannot solve for $C from peakConcurrent × perTrade. Per SPEC § D2, the engine falls back to a deterministic $1,000,000 default for auto + equal_weight rows. The persisted starting_capital_source is still "auto" (no separate discriminator); the methodology body describes how "auto" resolves under each Sizing arm. Operators who care about the realized capital level should pin --starting-capital explicitly.
Operator-pinned $Cstarting_capital_source=pinned
Operator overrides the auto formula — useful for modeling a specific account size.
When the operator pins startingCapital the engine honors it unconditionally — no clamping, no rejection, no consistency check against peak concurrent occupancy. If observed peak deployment exceeds 60% of the pinned $C the engine fires a CAPACITY-BOUND banner (see below): the strategy is running against a meaningful capacity constraint and the equity curve reflects that constraint as much as it reflects strategy alpha. Pinned runs are the right tool when you want to model a specific account size (e.g., "what would $50K have done on this strategy?") rather than the cross-strategy-comparable auto-resolved default.
Peak deployment fractionpeak_deployment_fraction
The observed maximum fraction of $C deployed in open positions across the run.
Defined as max(deployedCapital_d) / resolvedStartingCapital over the run's trading days. Persisted as a scalar column on backtest_results so the admin browse can surface it without loading the heavy daily_portfolio_values jsonb. Auto-resolved runs target 0.30 by construction; pinned runs can land anywhere from near-zero (lightly-traded strategy in a generous account) to well above 0.6 (capacity-constrained — see CAPACITY-BOUND below).
CAPACITY-BOUND banner
Fires when an operator-pinned $C produces peak deployment greater than 60%.
Honest framing: at that deployment level the equity curve reflects a capacity constraint as much as it reflects strategy edge. The banner is one of six v1.1.0 banner templates pinned verbatim in the hashed methodology body — text changes flip the methodology hash. Auto-resolved runs never fire this banner because the auto formula targets 30% deployment by construction; it's a pinned-only signal.
Equity-curve cumulative returncumulative_return
Capital-bound. Answers "what would a $C account have done." Sensitive to cash drag.
V_T / V_0 − 1 where V_t is the daily equity value (cash + Σ open-position MTM). Sensitive to cash drag — the idle fraction of $C dilutes per-position returns proportionally to 1 − deployment_fraction. The operator-facing "deliverable performance at this capacity" answer. The companion max_drawdown number is peak-to-trough on the same V_t series; annualized_return is the standard (V_T/V_0)^(252/n) − 1 annualization on the equity curve. All three are NULL on walk-forward rows (per-fold capital resets break the concatenated time series — banner 5 fires).
CTP cumulative return (Calendar-Time Portfolio)ctp_cumulative_return
Capital-free. Cross-strategy comparable. The academic edge measure.
Equal-weight mean per-day return across all positions open during each period; cumulative product across periods. Per Fama (1998) and Mitchell & Stafford (2000) the canonical "is there edge" measure in cross-sectional event-study literature; engineering precedent in NautilusTrader's position-returns-vs-portfolio-returns distinction. Independent of $C — two runs of the same strategy at different starting capital produce identical CTP returns. Use CTP when comparing strategies (the right cross-strategy edge comparison); use the equity-curve return when projecting against a real account size.
Why fixed-notional with no compounding
Path-independence is the deliberate property; cross-strategy comparability is the secondary benefit.
Each trigger always allocates exactly perTrade regardless of the current V_t — the engine does NOT compound. Two consequences. (a) Trade-by-trade dollar P&L is path-independent: a winning trade early in the run doesn't make subsequent trades larger; the same trigger sequence produces the same dollar outcomes regardless of order. (b) The per-trade Sharpe and holding-period Sharpe are honest reflections of trade-level edge rather than capital-growth artifacts. Percent-of-equity compounding is an alternative capital-accounting convention deferred to a future SPEC that pins it behind a config flag — it would fork the methodology contract (different drawdown semantics, different return semantics, different walk-forward behavior). V1.1.0 deliberately ships only the fixed-notional posture.
Entry arms
V1 hard-pins Entry to next_trading_day_open. The 'why' is in the long text — intraday entry primitives leak future information through the trigger evaluation.
Open of the next trading daynext_trading_day_open
Trades enter at the open of the next NYSE trading day after the trigger fires.
Trades enter at the open of the next NYSE trading day after the trigger fires. This is hard-pinned in V1 to avoid look-ahead bias: a strategy may only act on substrate visible at the prior trading day's market close, not on intraday or same-day signals. The 'why' matters more than the rule — backtests that allow intraday entries silently leak future information into the trigger evaluation, producing inflated returns that won't replicate live.
Exit arms
V1.3.0 ships two Exit arms: n_trading_days_close (fixed-horizon hold, extended in-place with OPTIONAL stop-loss and profit-target short-circuits per Phase 25.22 § D1) and hold_until_event_match (catalyst-anchored exit per § D3 — hold until eventTrigger fires on the same ticker OR maxHoldDays cap reached). The two arms do NOT compose in V1 — the timing discriminator picks exactly one. Signal-decay exits remain V2 (see Out-of-scope V1 below).
Close N trading days after entryn_trading_days_close
Trades exit at the close of the Nth NYSE trading day after entry (holdDays). Phase 25.22 v1.3.0 adds optional stop / target.
Trades exit at the close of the Nth NYSE trading day after entry, where N = holdDays. Note: holdDays=1 means the entry-day close (the earliest possible exit given next-trading-day-open entry); holdDays=5 means roughly one calendar week; holdDays=21 means roughly one calendar month. The exit price is the adjusted close on the exit date. Phase 25.22 v1.3.0 (SPEC § D1) augments this arm in-place with two OPTIONAL short-circuit fields — stopLossPct (negative, e.g., -0.10) and profitTargetPct (positive, e.g., 0.15) — evaluated daily against adj_close inside the hold window. Per § D1 tie-breaker the engine checks profitTargetPct first per trading day, then stopLossPct; the same daily close cannot satisfy both. The daily-close-only evaluation is a conservative under-count vs real-world intraday stops (methodology body discloses honestly: V1 has no intraday substrate, so 'hit on the way to a worse close' is invisible). Trades that ride the full window without firing either short-circuit emit Trade.exitReason='hold_complete'; short-circuit firings emit 'profit_target' / 'stop_loss' respectively.
Hold until catalyst event matcheshold_until_event_match
Hold until eventTrigger fires on the same ticker OR maxHoldDays cap reached (whichever first).
Phase 25.22 v1.3.0 catalyst-anchored exit (SPEC § D3). After entry the engine watches the same ticker each trading day for the configured eventTrigger; the first day the trigger fires within the maxHoldDays cap, the position exits at the next-trading-day open and Trade.exitReason='catalyst_match'. If the cap is reached without a hit, the position exits at the cap day's close and exitReason='hold_complete'. V1 child-arm restriction (loader-enforced per the Phase 25.15 windowed_and precedent): the eventTrigger MUST be an event_match or alert_fires BaseTrigger arm — signal_threshold children are V2-deferred for the same substrate-bypass-risk reason composition arms decline them (honest cross-detection requires re-running buildMarketStateAt(T-N) against prior state). The maxHoldDays cap is MANDATORY (≥1, no default) — an unbounded hold-until-event-match would be an infinite-wait primitive that V1 declines to ship. The realized hold duration varies per trade, so AggregateMetrics.holdDaysForSharpe derives from the realized-mean trading-day hold across closed trades (Phase 25.22 § D3a) instead of the static holdDays scalar; SHARPE ANNUALIZED banner template substitutes that realized mean for ${h}. Catalyst-anchored exits do NOT compose with stop-loss or profit-target in V1 (mutually exclusive via the timing discriminator).
Parameters
The free-form numeric and string fields the Strategy Editor exposes — these are the dials inside each arm's sub-form. Every value enters strategy_hash, so changing one produces a structurally distinct strategy.
Per-trade dollar amountperTrade
Dollar amount allocated to each trade. Positions never block; the engine sizes a hypothetical capital pool to fit the peak.
The dollar amount the engine allocates to each trade. Under V1's fixed-notional sizing positions never displace each other and triggers never wait for a cash reserve — the engine resolves a hypothetical capital pool ($C) large enough to admit the peak concurrent position count at the perTrade allocation, then marks positions to market against it. Auto-resolved $C targets ~30% peak deployment; an operator-pinned $C is honored as-is and fires a CAPACITY-BOUND banner if observed peak deployment exceeds 60%. Typical values: $10,000 (small-scale) to $100,000 (institutional-scale). The number enters strategy_hash, so changing it produces a structurally distinct strategy; $C does not (capacity is a runtime parameter, not strategy identity).
Hold days (trading days)holdDays
Number of NYSE trading days the position is held before close-of-day exit.
How many NYSE trading days to hold the position before exiting at the close. NOT calendar days. holdDays=1 means the entry-day close (the earliest possible exit given next-trading-day-open entry); holdDays=5 ≈ 1 calendar week; holdDays=21 ≈ 1 calendar month. The hold window is also the h parameter in the holding-period-Sharpe annualization formula (√(252/h)) under methodology v1.1.0+, so it affects how the headline Sharpe is computed.
Window (trading days)windowDays
Trading-day window for the two-condition composition. 0 = same day; 5 ≈ 1 week; 21 ≈ 1 month.
Trading-day backward window the engine searches when composing two conditions. 0 = strict same-trading-day simultaneity; 5 ≈ 1 calendar week; 21 ≈ 1 calendar month. Window arithmetic uses NYSE trading days (skips weekends and holidays) via nthTradingDayBefore from src/hit-rate/nyse-calendar.ts; calendar-day windowing is NEVER used. For windowed_and the engine searches forward and backward (commutative); for windowed_and_not the search is strictly backward from childA's fire date (asymmetric).
Minimum signal scoreminSignalScore
Threshold the signal score must reach (or cross upward) for the trigger to fire. Range 0–1.
The minimum signal_score the trigger requires before firing. Signal scores range 0–1. KOL sentiment typically peaks at 0.6–0.8; payer commentary at 0.5–0.7. Setting the threshold too high (e.g., 0.9) produces few-to-zero triggers; setting it too low (e.g., 0.2) fires on background noise. Start in the typical-peak range for the signal type and walk-forward to find the threshold that produces durable hit-rate improvement across folds.
Require upward crossingmustCrossUpward
If checked, fires only when the score crosses UP through the threshold; if unchecked, any at-or-above fires.
When checked (mustCrossUpward=true): the trigger fires only when the prior trading day's score was BELOW the threshold and today's score is AT or ABOVE — i.e., an upward crossing. The engine retains a sliding two-day window to detect this. When unchecked (mustCrossUpward=false): the trigger fires every day the score is at or above the threshold, including days the score has been stably elevated for weeks. Upward-crossing strategies are leaner (fewer trades; cleaner edge) but miss persistent-signal regimes; non-crossing strategies trade more often and can over-fire on flat signal lines.
Entity scopeentityType
Whether the signal is keyed by ticker (e.g., AAPL) or by substance (e.g., semaglutide).
Selects the entity granularity the signal is keyed by. ticker keys signals at the company level (e.g., LLY); substance keys signals at the molecule level (e.g., tirzepatide), which the engine then joins back to the watchlist via substance↔ticker relations. Substance-keyed signals are useful when the operator's strategy is about a drug's commercial trajectory independent of the company surface (e.g., a GLP-1-class signal that touches multiple tickers).
Entry timingentry.timing
Hard-pinned to next trading day open. Avoids look-ahead bias by acting only on substrate visible at decision time.
Trades enter at the open of the next NYSE trading day after the trigger fires. This is hard-pinned in V1 — there is no intraday entry option. The 'why' matters more than the rule: backtests that allow same-day or intraday entries silently leak future information into the trigger evaluation. By the time an event is visible to the engine, the market has already partially priced it; entering at the next-day open is the earliest honest acting moment. Intraday entry primitives are deferred to a future SPEC that pins their look-ahead-bias safety.
Strategy IDid
Kebab-case identifier used as the row label on /admin/backtests. ≤ 64 chars.
The kebab-case identifier (e.g., kol-sentiment-cross-0.7) that becomes the row label on the /admin/backtests browse page until the operator switches to strategy_hash filtering. Keep it short and human-meaningful — it's the first thing you'll see when scanning past runs. Two strategies with the same id but different shapes produce different strategy_hash values, so the id is descriptive metadata, not a uniqueness constraint.
Strategy descriptiondescription
Free-form prose explaining the strategy's hypothesis. ≤ 200 chars. Renders on the detail page.
Free-form prose explaining the strategy's hypothesis or rationale (≤ 200 chars). The text lands in strategy_snapshot.description and renders on the /admin/backtests/[id] detail page so a reader returning weeks later can recall what the run was testing. Good descriptions name the hypothesis ('KOL-sentiment cross above 0.7 captures pre-announcement leak on watchlisted tickers') rather than just restating the parameters.
Stop-loss percentstopLossPct
Negative number (e.g., -0.10 = -10%). Daily-close evaluation; conservative under-count vs intraday stops.
Phase 25.22 v1.3.0 OPTIONAL stop-loss short-circuit on the n_trading_days_close Exit arm (SPEC § D1). Expressed as a negative number — -0.10 means -10%. The engine checks per trading day inside the hold window: if (adj_close_d − entryPrice) / entryPrice ≤ stopLossPct, the trade exits at that day's adj_close with Trade.exitReason='stop_loss'. Tie-breaker: per § D1, profitTargetPct is checked FIRST per day (positive close wins target); stopLossPct falls through second. The same daily close cannot satisfy both because stopLossPct < 0 < profitTargetPct (loader-enforced ordering). The V1 daily-close-only evaluation is a CONSERVATIVE UNDER-COUNT vs real-world intraday stops — a trader with a live stop order would exit on intraday touch, not end-of-day. The methodology body discloses this honestly; admin per-trade journal renders 'hold_complete' (not 'stop_loss') when the trade's worst intraday print would have hit but the daily close recovered. V2 candidate: high/low-aware evaluation when an intraday substrate exists.
Profit-target percentprofitTargetPct
Positive number (e.g., 0.15 = +15%). Per-day check first per § D1 tie-breaker.
Phase 25.22 v1.3.0 OPTIONAL profit-target short-circuit on the n_trading_days_close Exit arm (SPEC § D1). Expressed as a positive number — 0.15 means +15%. The engine checks per trading day inside the hold window: if (adj_close_d − entryPrice) / entryPrice ≥ profitTargetPct, the trade exits at that day's adj_close with Trade.exitReason='profit_target'. Tie-breaker: per § D1, profitTargetPct is checked FIRST per day (target wins), then stopLossPct. The same daily close cannot satisfy both because stopLossPct < 0 < profitTargetPct (loader-enforced ordering). Like stopLossPct, the daily-close evaluation is a conservative under-count vs intraday touches; the methodology body discloses this honestly. Composing profitTargetPct alone (without stopLossPct) is a valid 'ride to target or hold to N' shape; the trade falls through to hold_complete if the target never fires.
Weight fraction (of $C)weightFraction
(0, 1] — fraction of resolvedStartingCapital per position. Capacity-coupled.
Phase 25.22 v1.3.0 sizing parameter on the equal_weight Sizing arm (SPEC § D2). At entry the engine resolves perTrade = weightFraction × resolvedStartingCapital and persists the concrete dollar amount on the Trade record. Loader-validated as finite, > 0, ≤ 1. The capacity-coupled semantic is load-bearing: per Phase 25.22 § D2, equal_weight binds entry size to runtime $C, so two runs of the same strategy at different startingCapital produce identical strategy_hash (Strategy shape is hash input, runtime $C lives on BacktestRunContext) but materially different equity curves. The methodology body discloses this divergence honestly; Phase 25.19 sweep cells that vary startingCapital against an equal_weight strategy will share a hash but produce different output. Practical guidance: weightFraction > 0.5 is substrate-thin (only 1-2 positions ever open before deployment saturation); methodology body flags as uncommon without loader-rejecting. The Phase 25.13 CAPACITY-BOUND banner gains a Phase 25.22 arm that names weightFraction directly when peakDeploymentFraction > 0.6 AND sizing.kind === 'equal_weight'.
Max hold days (catalyst-anchored)maxHoldDays
Mandatory cap (≥1). Bounds the catalyst-wait window on the hold_until_event_match Exit arm.
Phase 25.22 v1.3.0 MANDATORY cap on the hold_until_event_match Exit arm (SPEC § D3). Positive integer (≥1). An unbounded hold-until-event-match would be an infinite-wait primitive — a catalyst that never materializes would leave the position open through config.to and exclude it from headline metrics via the hasOpenPosition flag. Mandating the cap forces strategy authors to bound the wait honestly and also bounds the engine's per-trade evaluation work (Phase 25.22 Task 0d two-tier bench probe established the per-day evaluator cost budget). When the cap is reached without a catalyst hit, Trade.exitReason='hold_complete' and the exit price is the adj_close at nthTradingDayAfter(entryDate, maxHoldDays); when the catalyst fires within the cap, exitReason='catalyst_match' and the exit price is the next-trading-day open after the catalyst day. Since the realized hold duration varies per trade, AggregateMetrics.holdDaysForSharpe derives from realized-mean hold (Phase 25.22 § D3a) instead of the static holdDays scalar.
Exit reasonexitReason
Why the trade exited: hold_complete | profit_target | stop_loss | catalyst_match.
Phase 25.22 v1.3.0 (SPEC § D1, § D3, § D8) — the discriminated-string field persisted on every Trade row by the v1.3.0+ engine, naming WHY the trade exited. Four arms: (a) 'hold_complete' for a natural N-trading-day-close exit on the n_trading_days_close arm OR a maxHoldDays cap reached on the hold_until_event_match arm without a catalyst hit; (b) 'profit_target' when n_trading_days_close.profitTargetPct fired (positive close ≥ target inside the hold window, § D1 tie-breaker wins target first per day); (c) 'stop_loss' when n_trading_days_close.stopLossPct fired (negative close ≤ stop inside the hold window, falls through after target check); (d) 'catalyst_match' when hold_until_event_match.eventTrigger fired on the same ticker within maxHoldDays. Pre-v1.3.0 persisted trades lack the field — the read-side back-compat path defaults to 'hold_complete' per § D8 + Edge Cases. Admin per-trade journal renders the exitReason inline so operators can answer 'why did this trade exit early?' without re-deriving from the price series.
Out of scope (V1)
What V1 deliberately defers. Each item is gated on a separate SPEC pass — most because they require non-trivial substrate or look-ahead-bias safety work. Hard-coded inline here (these shapes aren't enums today; there's nothing to drive a description map from).
Parameter sweep
Sweeping a parameter (e.g., minSignalScore from 0.3 to 0.9 in 0.1 increments) and walking-forward each cell to identify a durable choice is a Phase 25.19+ surface. V1 supports one strategy per run; the operator manually iterates.
V2 Sizing arm: signal_weighted
Signal-weighted sizing (position size proportional to the triggering signal's magnitude) is V2 — same V1-narrow-primitive discipline that gated equal_weight until Phase 25.22 v1.3.0. Phase 25.22 ships fixed_notional + equal_weight; signal_weighted awaits a future SPEC that pins the magnitude-to-dollar mapping convention.
V2 Exit primitives: trailing stops, signal-decay, time-of-day, composed stop+catalyst
Phase 25.22 v1.3.0 ships daily-close stop-loss + profit-target augmentations on n_trading_days_close and the catalyst-anchored hold_until_event_match arm. Still V2: trailing stops (the stop level drags upward with new highs), signal-decay exits (exit when the original triggering signal weakens below a threshold), time-of-day exits, AND composition of stop-loss/profit-target with hold_until_event_match (V1 treats the two Exit arms as mutually exclusive via the timing discriminator). Each shape compounds the per-arm semantics and warrants its own SPEC.
Rebalancing equal_weight semantics
Phase 25.22 v1.3.0's equal_weight arm sizes at entry-time as weightFraction × resolvedStartingCapital and does NOT rebalance existing positions when a new trigger fires. Alternative shapes considered in the SPEC (a: rebalance-on-entry across currently-open positions; b: bind to a position-count cap via perPositionFraction = 1 / maxConcurrent) are V2 candidates — each requires running-position bookkeeping or per-entry repricing that V1 declines.
V2 Trigger arms: N-ary AND, nested composition, OR composition, time-ordered AND
V1 composition is binary (windowed_and, windowed_and_not) and commutative-or-asymmetric only. N-ary AND (three or more conditions), nested composition (a windowed_and whose children are themselves windowed_ands), OR composition, and time-ordered AND (require A to precede B by ≥ N trading days) are all V2 — each shape compounds the canonicalization and substrate-bypass surface area.
Intraday entry primitives
V1 entry is hard-pinned to next_trading_day_open to avoid look-ahead bias (see Entry timing section). Intraday entries (open-of-trigger-day, close-of-trigger-day, +N-minutes-after-trigger) are V2 — they require a SPEC pass on look-ahead-bias safety because the engine would need substrate visibility at intraday granularity.
Intraday-aware stop-loss + profit-target evaluation
Phase 25.22 v1.3.0 stops and targets evaluate ONLY against adj_close — a CONSERVATIVE UNDER-COUNT vs real-world intraday touches. A trader with a live stop order would exit on intraday touch, not end-of-day; the methodology body discloses this honestly. Intraday-aware evaluation is V2 candidate, gated on an intraday substrate that V1 does not ship.