ADR-0012 documents the 34-slot confluence signal engine architecture: slot catalog, redundancy-aware rack evaluation, CandleProvider seam, closed-loop signal history, and picture-change alert producer. CONTEXT.md gains a new 'Confluence Signal Engine (M22)' glossary section.
42 KiB
Investor Flow — Domain Glossary
This file is the ubiquitous language for the Investor Flow project. It is a glossary, not a spec. Updated inline as terms are resolved during design (per the domain-modeling skill).
Status of what is built vs pending lives in docs/FUNCTIONAL_DESIGN.md and docs/TECH_DESIGN.md (audited 2026-07-18). Do not treat this glossary as a build checklist.
Product
Investor Flow — A multi-tenant investment management workbench for retail investors (positions, risk, market awareness, decision process). Helps users become better at awareness and risk management while managing - not a course/education app. Legal voice: educational publisher (ADR-0007). Local-first: Next SPA + Node/SQLite backend, Docker Compose. Voice: convex in process, soft in presentation. Workspace density (focused | standard | full) adapts nav and research detail from a short setup interview.
Primary Rule — Education, not investment advice. Investor Flow is an educational research terminal, not an investment adviser. It teaches how a disciplined investor reasons about a position; it never says "buy/sell/hold this." Every recommendation is a consideration + a question. RiskEngine recommendedActions are reworded (cut_to_cash → consider_reducing_position; trim_cluster → consider_rebalancing_cluster). SizingEngine outputs math, not instructions. Journal asks "what's your reasoning?" Alerts say "something changed," not "action needed." Every report closes with: "Educational analysis, not investment advice. Verify the underlying data; you are responsible for your own decisions." Legal posture: educational publisher. Formalized in ADR-0007. Every LLM prompt template and UI string reviewed against this rule ("Primary Rule lint"). When this conflicts with another principle, this wins.
Analyst Voice — The single house voice for every LLM Signal Summary, explainer, and alert. 70/25 blend of Mike Alfred (Alpine Fox LP — concentrated value conviction, ownership posture, plain-spoken directness) and Stanley Druckenmiller (macro-regime adaptation, asymmetric convexity). Rules: process over prediction; concentration posture; macro + company twin-lens; honest about uncertainty; P6 plain English; cite every claim to a cached source; confident when conviction exists, silent when it doesn't. Formalized in ADR-0005.
P6 — Accessible-but-authored voice. Core UX principle. Every user-facing string reads as plain English to a non-finance reader while staying professional. No orphan jargon (every term glossed in-screen); no baby talk; no unexplained acronyms. Complexity level adapts depth, not voice.
Strategy & Sizing
Strategy — A named bundle of { Regime gate, Setup, Risk Policy }, reusable across symbols. A TradePlan is an instance of a Strategy applied to a symbol at a time.
Regime — A classification of market state (trending-up / trending-down / range-bound) over an index/timeframe, used to gate trading posture and sizing.
Setup — A parameterized, repeatable entry/exit checklist applied to a symbol situation.
Risk Policy — Sizing + stop-width + defined-risk rules applied when a setup fires.
Screener — A tool that evaluates a universe of symbols against one or more Strategies' entry conditions and outputs matches. (A screener run is a read-only backtest at "now".)
Backtest — Running a Strategy against cached historical OHLCV to compute performance. Cache-only; no live calls. Same engine as Screener.
Position Sizing — The function producing share quantity given a Strategy, risk%, stop, account, live portfolio, and regime. Multi-layered (Layers 0–4).
Risk Per Trade — Dollars an account is willing to lose if a plan's stop is hit, before any size is computed. Default 1% for beginners.
Conviction Tier — A_STAR / A / B / C — the user-graded quality of a setup; multiplies the base risk fraction (×3 / ×2 / ×1 / ×0.5). Locked at lower tiers until per-tier win-rate statistics justify unlocking.
Sizing Unlock — A workflow state: complexity-tier elevation earned by accumulating profitable trades at a sub-tier (20 profitable B → unlock A; 10 profitable A → unlock A_STAR). Tunable per-user.
Macro Regime Gate — When regime trending-down = ×0.5 size; ranging = no A_STAR; trending-up = normal + A_STAR unlocked. Default: auto-cap with one-tap override requiring a written reason.
Correlation Cluster — A group of holdings sharing a single macro/factor driver (e.g., all BTC miners). Aggregated exposure capped; beginners get hard caps, intermediates get warnings.
Institutional & Insider
Quarter (institutional module) — The reported calendar quarter a 13F snapshot is as of. Buy-zone/sell-zone estimates reconstruct institutional transaction likelihood within that quarter from the snapshot diff + volume-weighted price action.
Institutional Footprint — Per-symbol combined ownership picture from all four SEC forms (13F, 13D, 13G, Form 4), with every owner row classified by holder type.
Holder Class — One of: Passive Index, Activist, Active Conviction, Market-maker/Hedger, Insider. Inferred from which SEC form(s) filed + CIK entity metadata (SIC codes).
Net Active Conviction Δ — Per-symbol QoQ share change summed across Active Conviction + Activist classes only. Excludes passive index and MM/hedger noise. The ranked signal on the dashboard.
Holder Snapshot Diff — Quarter-over-quarter Δ per institution per symbol, derived from consecutive 13F filings.
Insider Transaction — A single Form 4 transaction by an officer, director, or 10%+ holder. Classified: Informed Buy / Informed Sell / Routine.
Informed Buy — Open-market buy (code P) NOT under a Rule 10b5-1 plan. Highest-signal insider bull.
Informed Sell — Open-market sell (code S) NOT under 10b5-1. Negative signal; alert by default.
Routine Insider Transaction — Any Form 4 transaction under 10b5-1, OR an option exercise (M), grant (A), or vesting event. Pre-scheduled/compensation-driven; low signal; hidden by default for beginners.
Buy-Zone Estimate — Volume-weighted price band during a quarter in which an institution likely accumulated/reduced. An estimate, always tagged "estimated" in the UI.
Insider Activity Stream — Real-time per-symbol ledger of Form 4 filings, classified, with Informed events surfaced first and alertable. Own module (M5), separate from quarterly snapshot (M4).
Sector Rotation
Sector Rotation — Movement of capital between market sectors. The module surfaces both realized (RS-rank reshuffle over N weeks) and incipient (early RS-slope turn + breadth thrust before price moves) rotation.
Rotation Phase — A label on detected rotation: Accumulation → Expansion → Distribution → Markdown, with a confidence score.
Rotation Signal History — Every incipient rotation signal is logged and later resolved as Real or False-alarm. Two-stage resolution (γ): price follow-through confirms first (within N=4 weeks, deterministic); institutional flow confirms later (quarter-end, durable). Enables "which signal types were reliable" learning.
Ticker
Ticker Kind — equity | crypto | etf | index. Gates which modules apply (e.g., crypto excluded from SEC/13F/insider modules). BTC kept for price/sentiment only.
Symbol — A ticker as a first-class identity (e.g., IREN). Carries a Ticker Kind, display name, sector/industry, exchange, and — for equities — an Issuer CIK. The canonical record is the symbols table; it is what autocomplete resolves against when a user adds a symbol to a watchlist or searches at the top.
Issuer — The company a Symbol represents (e.g., the company behind IREN). Identified by the SEC Issuer CIK (the company's own SEC identity — files its own 10-Ks, insider Form 4s, 13D/G). Distinct from a Filer CIK, which identifies a fund that files 13Fs. Same CIK identifier space, two roles: issuer = the company the symbol stands for; filer = the fund that reports holdings.
Symbol Search Index — The autocomplete corpus for adding symbols and top-bar search. Sources: the symbols table (issuer CIK, name, sector, ticker kind) joined with fund-holdings edges (fund_position_records) so a result can surface "also held by [Tracked Fund]" from the fund side. Local-first per ADR-0009: no live vendor search on the request path. Scope: search results are symbol-only — the fund-holdings edge is NOT shown inline in autocomplete; it surfaces on the symbol's overview page after the symbol is added.
Add-Symbol Resolve Rule — How watchlist add behaves when a typed string does/doesn't match the Symbol Search Index (hybrid): a known-symbol match adds instantly with confidence; an unmatched string is a soft-block — the app asks "this symbol isn't in the SEC registry — add anyway?" before adding, then hydrates metadata in the background via the adapter queue (quote, issuer CIK if findable). The row appears immediately, marked "resolving…" until hydration lands. Strictness is a question, not a wall.
Fund-Holdings Strip — The symbol-page section answering "who holds this?" Two tiers: (1) tracked funds first — the M21 fund_position_records edge, each with its weight in the fund's disclosed book (e.g., "Alpine Fox — 3.2% of book"); (2) all institutions second — an expandable "N other institutions report holding this (13F)" line sourced from the shared institution_filings cache (M4). Tier 1 is curated and deliberate; tier 2 is broad and noisy — order encodes trust.
Issuer CIK Seed (company_tickers.json) — The SEC's company_tickers.json bulk file (https://www.sec.gov/files/company_tickers.json) is the canonical seed for the Symbol Search Index: ~12k+ US tickers, each paired with issuer CIK, name, and exchange. SEC overwrites the file in place daily (start of trading day, ~5:30am ET; no historical versions). Refresh cadence = once per trading day, scheduled, never on the request path (ADR-0009 stale-while-revalidate pattern). Materialization = a scheduled queue job (sec-company-tickers source kind) that upserts into the symbols table.
Symbol-Metadata Merge Policy — Two writers on the symbols table, two ownership domains, no overwrite war. The SEC seed fills only what it's authoritative for: cik, name, exchange. It never touches yfinance-hydrated sector, industry, peers (absent from the SEC file), and never downgrades an existing ticker_kind (etf/crypto/index preserved — kind gates module applicability). Rows absent from the SEC file (crypto, some ETFs, indexes) are never purged — the SEC universe is a subset, not the whole. Background hydration (Q1) owns classification/descriptive metadata; the SEC seed owns issuer identity.
Screener (two modules)
Filter Screener (M15a) — A TradingView/Finviz-style screener: user writes ad-hoc filter expressions (descriptive, technical, fundamental, events, ownership, sentiment) over a universe; outputs matching symbols with "why matched." Beginner-immediate. No Strategy required. Saved filter sets are per-user (Tier C). Not backtested. Universe: tiered (watchlist first, opt-in broader scan scoped by sector).
Strategy Screener (M15b) — A screener that evaluates a chosen Strategy bundle's entry conditions across a universe; outputs symbols where the Strategy fires, with conviction-strength (how many entry conditions true). Backtestable. Requires an authored Strategy (gated by Conviction Tier unlocks). Output: symbol + which conditions fired + one-tap into Symbol Overview workbench.
UniverseEvaluator — Shared deep module iterating a symbol universe and applying a pure predicate (a compiled filter expression OR a Strategy's entry conditions) against cached data. Powers both M15a (filter predicate) and M15b (Strategy predicate). One interface, two callers.
Screener output policy (both modules): symbol + "why matched" + one-tap "open in workbench" (drop symbol into Symbol Overview). The screener shortens time-to-conviction; it is never the final answer.
Options Convexity Sleeve (M17)
Options Convexity Sleeve (M17) — A portfolio module applying a small defined-risk options overlay to a core holding — for income (covered calls), cheaper entry (cash-secured puts), downside insurance (protective puts/collars), or leveraged thesis (long LEAPS). Never standalone directional. (Research-grounded: Spitznagel/Universa tail-hedging, Pabrai cash-secured entry, Ackman rate-hedge overlay, Druckenmiller cheap-convexity, Buffett index-put seller.)
Sleeve Risk Budget — Per-thesis combined budget for stock + options risk (e.g. 1–5% of portfolio). SizingEngine enforces the combined ceiling so options can't quietly swell.
Convexity Posture — A user's options advancement state: Off → Covered Income → Cash-Secured Entry → Insurance Sleeve → LEAPS Conviction. Unlocks progressively by Sizing Unlock (mirrors Conviction Tier). Default Off (Alfred: "I don't think most people should use options tbh").
Defined-Risk Only — Hard module constraint: strategies with unbounded loss (naked short calls, naked short straddles, undefined-risk structures) are physically blocked. Beginner guardrail; never disabled for beginners.
Tail Convexity — A small portfolio sleeve (~1–3%/yr) of long-dated deep-OTM puts on broad indices OR a correlation cluster (Spitznagel/Universa model). Pays small premium most months, asymmetric payout in tail events — insurance, not speculation.
IV Regime Gate (within M17) — Modulates allowed overlays by IV Percentile: covered calls when IV high (juicy premium); protective puts / LEAPS when IV low (cheap insurance). Never buy convexity when it's expensive.
Mirror Portfolio (M21)
Tracked Fund — A specific fund (e.g., Alpine Fox LLC) the operator follows fund-first: the inverse of the symbol-first Institutional module. Identified by CIK for SEC data (13F) and by the manager's X handle(s) for the live channel. The fund and the person are distinct entities: the 13F is filed by the fund (CIK); X posts come from the manager. Two channels, two authorities, two latencies — official but stale (13F) vs live but self-reported (X).
13F Record — A single SEC-confirmed holding row for a tracked fund: CIK, symbol, shares, market value, reported quarter. Quarterly + ~45-day filing lag; as of the quarter end. Shows drift, not decision. Reuses the shared institution_filings cache (ADR-0004). Avoid: holding (ambiguous across channels).
Position Capture — A self-reported snapshot of the fund's current total position in one ticker, with actual numbers: total shares, market value, and cost basis. Posted by the manager on X (typically as a screenshot); recorded with post date + evidence link (tweet URL). Dated state on the position's timeline — it supersedes earlier captures for the current view but never overwrites history. Avoid: screenshot (implementation detail), position update (ambiguous).
Trade Claim — An X post stating a position delta with real numbers ("added n shares at x average price"). Supplementary color, never authoritative state; recorded as a dated delta that should reconcile with the next Position Capture (prev total + delta ≈ new total; avg-cost math must close).
Cost Basis — The average cost per share shown in a Position Capture. The decision-level measure (what the manager deployed), contrasted with market value which is drift. What makes a fund's construction readable through captures and invisible in 13Fs alone.
Live Book — The current-state view of a Tracked Fund's holdings: the most recent record per position, mixing 13F Records and Position Captures, each labeled with its source and as-of date. "Estimated/live" for captures, "SEC-confirmed (quarter-end)" for 13F.
X Post Classes (fund feed) — Every manager post splits into exactly two classes: Position Captures / Trade Claims (actual numbers → become records) and commentary (thesis talk, macro takes, no numbers → never a record, no matter how strongly worded). Classification is per-post; the app already LLM-classifies posts (sentiment) so there's a precedent.
Disclosed Book — The fund's visible holdings: 13F Records + Position Captures. Weights are computed over the disclosed book only — cash, hedges, and non-disclosed assets are unknown and labeled as such. Avoid: portfolio (implies the whole fund, which we can't see).
Mirror Portfolio — The module's core job: the user's stated goal of replicating a Tracked Fund's book. The target = the fund's Disclosed Book expressed as weights; applied to the user's own equity it answers what to buy, when (the X edge: captures are live, 13F lags ~45 days), and how many shares to match each weight. Per-user math over shared fund data; never advice (ADR-0007 line: "to match this weight, buy N shares" is a calculation for a user-stated goal, not a recommendation). Whole-book scope: the mirror converges the user's entire portfolio to the fund's weights, scaled to a user-entered capital base (suggested default ~$200k; accepts ~$20k accounts). The fund's AUM never enters the math — only the user's base does.
Mirror Floor — The minimum position threshold in the Practical Mirror: positions below ~$500 or ~0.5% weight (whichever is larger) are excluded from the mirror and reported as one "excluded (below minimum)" line. Fractional shares allowed but rounded to a sensible tick. The floor is a setup choice, shown honestly.
Mirror Target Rule — The mirror's target per position = the most recent record by as-of date, regardless of source. A Feb capture beats a Dec 31 13F; a Mar 31 13F beats a Feb capture. Captures and 13F Records compete on recency, not authority (no badges, no verdicts — option 2 reconciliation).
Mirror Diff — The delta between the fund's current target weights and the user's current holdings, recomputed on every new 13F Record or Position Capture. The "what to trade to stay matched" view: position, weight change, shares to buy/sell.
Fund Performance (mirror) — The disclosed book marked to market (equity curve, estimated from disclosed holdings) plus position-level unrealized P&L vs Cost Basis from captures. Dataroma-style returns with the unique cost-basis anchor nobody else has. Always labeled "estimated from disclosed holdings."
Manager Insider Activity — Form 4 transactions filed by the manager personally (as officer/director/10% holder), cross-referenced by insider_name against the shared insider_transactions cache. Surfaced on the fund page as a conviction-context strip ("fund holds IREN; manager is a director; latest Form 4: informed buy"), alongside 13D/13G events by the fund's CIK. Never part of the Disclosed Book and never moves the mirror — the fund's book changes only on fund records (13F Records, Position Captures); the manager's personal account is not the fund's book.
Macro Module (M18)
Macro Module (M18) — Fourth pillar module: calendar of high-impact macro events (M18a), current-regime classifier (M18b), portfolio-impact commentary (M18c), and regime history (M18d). The Druckenmiller 25% lens given dedicated surface. Reads macro + connects to portfolio; never recommends a macro trade (the Alfred caution extends to macro-trading). Leaves action to Conviction Tier + Convexity Posture gates.
Macro Regime — A label classified by M18b (trending-up / trending-down / range-bound / structurally-shifting) from yield curve, inflation, rate path, liquidity, breadth. Consumed by the Macro Regime Gate (SizingEngine Layer 4). M18b is the classifier the gate depends on.
Portfolio Impact Commentary — LLM-generated (Druckenmiller lens), two-horizon: short-term reaction risk (per-event, per-correlation-cluster, with historical averages + sample size + disclaimer, never a forecast) and long-term structural read (regime-shift framing). Cites M18 regime + the user's actual holdings. Always on (matches Analyst Voice "always on" decision).
Confluence Signal Engine (M22)
Confluence Signal Engine — The multi-axis evidence aggregator that produces a per-symbol "picture quality" (strong/moderate/weak-bullish, mixed, weak/moderate/strong-bearish, sparse) from34 independently-evaluable slots across 6 families: technical (15), institutional (5), macro (5), seasonal (5), flows (3), sentiment (1). ADR-0007: describes the picture, never recommends action. ADR-0012.
Slot — A named, independently-evaluable check whose firing state contributes bullish or bearish evidence to a confluence rack. Each slot has a SlotBody (bull / bear / exit), a SlotFamily, a SlotGranularity (1d / 1wk), and an ADR-safe explain note (evidence sentence, never a directive). The 34-slot catalog is defined in confluenceSlots.ts.
Rack — A named subset of the 34 slots that evaluates a symbol's confluence. Can be a system preset (Full Confluence, Technical Momentum, Macro+Flows+Sentiment) or user-created. A rack evaluation produces the redundancy-discounted evidence totals and picture quality label.
Redundancy Group — A set of slots that measure the same underlying condition (e.g. goldenCross + trendAlignment + pullbackToEMA21 all measure trend state). Evidence within a group decays geometrically (1 + 0.5 + 0.25 ...) so correlated signals count once, not triple. Defined in confluenceLibrary.ts.
Picture Quality — The evidence-based label for a symbol's confluence: strong/moderate/weak-bullish, mixed, weak/moderate/strong-bearish, sparse. Labeled from redundancy-discounted evidence totals using direction ratio (0.6) and magnitude thresholds (strong ≥ 4.0, moderate ≥ 2.0, sparse < 1.0 total evidence).
CandleProvider — The seam that resolves a symbol's candles per-granularity (1d/1wk) from the cache, with a realtime fold-in of the freshest live quote. Used by confluence evaluators instead of cache.get inline so a future realtime/replay source can slot in without touching slot logic. candleProvider.ts.
Signal History — The closed-loop log: every slot fire is recorded with the as-of date, rack, and picture quality. A resolver later checks whether price moved the expected direction over 4 weeks (bull → up, bear/exit → down). Verdicts: real (confirmed), false_alarm, or deferred (not enough forward bars). Per-slot reliability weights (0.5–1.25) allow the rack to self-tune.
Reliability Weight — A 0.5–1.25 multiplier applied to a slot's evidence based on its historical follow-through: ≥8 resolved fires at ≥90% hit rate → 1.25×; <2 resolved or ≤50% → 0.5×; thin sample → 0.75×. The rack can multiply per-slot evidence by this to self-tune.
Confluence Change — A detected shift in picture quality tier or net evidence (≥ 0.35 shift). Driven by the confluence_change alert producer. Throttled to 5/hr. ADR-0007 framing: "the picture has changed," never "act now."
Confluence Universe — The 15 research symbols + SPY benchmark that the confluence engine tracks: PLTR, NVDA, AMD, AAPL, MSFT, SMH, XOM, JPM, UNH, COST, AMZN, CAT, LMT, LIN, NEE. Pinned into the demand set on startup.
Regime History — Timeline of regime classifications over time; cross-references Rotation Signal History. Pedagogy for "did we detect the shift correctly" — beginner learns which classifier calls were early vs whipsawed.
LLM Data Provenance (ADR-0006)
ornith — The default LLM provider for Investor Flow: a remote-hosted, operator-owned, OpenAI-compatible REST endpoint. Configured is_local=true in llm_providers so it serves both public and sensitive features (ADR-0006 Data-Classification Gate). Env vars ORNITH_LLM_URL + ORNITH_LLM_API_KEY injected via Docker Compose secrets. Formalized in ADR-0008. The provider itself is pluggable; ornith is the v1 default, not a hard dependency.
LLM Data Provenance — Three hard constraints enforcing no-training-on-user-data: (1) Production LLM Gateway defaults to a local OpenAI-compatible endpoint; non-local providers require explicit operator override + documented provenance review. (2) Sensitive user data (portfolio positions, trade plans, journal, SEC content, sentiment annotations, saved posts, anything tagged ownerId or Tier C) is never routed through external LLM providers by code-level data-classification gate, not a runtime toggle. (3) Developer conduct: never paste live user-data into external AI-assistant prompts during the build; use fixtures/synthetic data only.
Local-first default — The Gateway's baseURL defaults to a local endpoint (Ollama/vLLM on host or LAN); the only provider config loaded by default. "Local" = localhost, 127.0.0.1, ::1, or LOCAL_LLM_SUBNETS env override.
Data-Classification Gate — LLMGateway.classifyPayload(payload): 'public_safe' | 'sensitive' runs before any provider dispatch. Sensitive payloads routed to non-local providers throw SensitiveDataBlockedError and never leave the host. A code-level guarantee, tested via property tests over a fuzz corpus.
Ticker-Data Dedupe (Architectural Pattern)
Content-Addressed Caching — All fetched market data is cached by a content-addressed key (e.g., yfinance:quote:IREN). Two users fetching the same symbol collapse to the same row; duplicates don't happen by construction. Tier A tables (price/candles/options/filings/13F) have no ownerId and are shared.
Demand Set — The set of symbols that currently have ≥1 user tracking them (watchlist ∪ open portfolio holdings). The adapter queue only schedules fetches for symbols in the demand set — zero wasted bandwidth on untracked symbols. Cost scales with the breadth of the demand set, not user count (a million users all tracking NVDA = still one NVDA fetch per staleness window).
Refcount — Per-symbol demand counter, bumped +1 when a user tracks the symbol, −1 when they untrack. Refcount → 0 halts live/sentiment refresh for that symbol; historical immutable data (backtest OHLCV, SEC filings) stays cached regardless because backtests/filings need it forever.
Stale-While-Revalidate — The SPA reads from cache instantly and the backend silently schedules a background refresh if the row is past its staleness window — the user never blocks on a live fetch. Duplicate concurrent cache-misses for the same key collapse to ONE fetch (multi-tenant dedupe per ADR-0004).
Rate-Limit-First Data Plane (ADR-0009) — Every vendor (Yahoo, X, FRED, SEC, Reddit) has a short rate limit. Design for that: request path serves cache / static first; only AdapterQueue does outbound fetches; a 429 cools down the entire source for minutes (not a 2s job retry); demand set bounds work; stale UI beats empty UI that thrash-retries.
P7 — Visualizations teach mechanics, never decorate outcomes
P7 — Core UX principle (added alongside P6). Every visualization makes a mechanical relationship more legible, never more exciting. Visuals can teach or manipulate (Robinhood's confetti SEC fine is the cautionary tale). The four guardrails:
- G1 — Teach the mechanic, don't decorate the outcome. A chart showing where Citadel accumulated within the quarterly price band teaches; a "+15%!" bouncing number celebrates. Alfred-lean voice explains mechanics, never celebrates results.
- G2 — Every chart has a one-line "what this tells you." No naked visualization. Each chart/heatmap/payoff diagram carries a P6 plain-English caption written at design time. If we can't write the lesson, the viz is decoration, not a feature — drop it.
- G3 — Color-blind / accessibility-safe by default. Red/green for P/L is dangerous (8% of men). Use shape + color + label triads: institutional adds = up-triangles in blue, reduces = down-triangles in amber, text labels always present. Recharts/visx support custom shapes natively.
- G4 — Annotated data viz over abstract 仪表盘. The LLM-generated Signal Summary produces annotated chart overlays — summary text has chart positions tagged. One annotated story, not 5 detached widgets.
Anti-gamification rules (locked)
Explicit "do not build" list — written down so the build doesn't accidentally Robinhood itself:
- No trade-animation confetti / win-streak badges / "you did it!" celebrations — Robinhood was SEC-fined for this.
- No 3D / decorative gauges — chart-porn, no mechanic taught.
- No real-time candle-minute flickering — encourages trader-brain, not investor-brain. Daily candle with a steady quote line is enough.
- No outcome-celebration visuals. Sizing/backtest viz shows mechanics (drawdown is more important than win rate), not trophies.
Canonical visualizations (locked — the curated 10)
Each is curated at design time with its one-line lesson (G2). Not an open-ended viz library.
- Annotated Price Chart — candles + Form 4 insider buy ▲ markers + institutional buy-zone shaded band + macro event markers + key levels overlaid. Lesson: prices move WITH insiders/institutions/events, never alone.
- Sector Rotation Heatmap — sectors × weeks grid, color = RS rank, cells pulse on phase transitions. Lesson: money rotates between sectors; early signs are subtle.
- Rotation Phase Timeline — horizontal stream of Accumulation→Expansion→Distribution→Markdown markers. Lesson: rotation is a phase sequence, not an instant.
- Options Payoff Diagram — today + at-expiry + early curves, max loss/gain shaded, breakeven marker. Lesson: an option position is a SHAPE not a bet; defined-risk is visibly bounded.
- Institutional Footprint Stacked Bar — per-symbol holder-% by Holder Class, color-coded (MM/hedger vs Active Conviction visually distinct). Lesson: not all ownership is conviction.
- QoQ Institutional Δ Chart — per-filer bars over a price-range overlay at the band they likely transacted at. Lesson: institutions move over a quarter, on a price band.
- Correlation Cluster Treemap — your holdings grouped by factor driver; cluster size = exposure $. Lesson: 5 holdings != 5 bets; factor risk (the 2008 lesson).
- Sizing Decision Tree — account → risk-per-trade → tier multiplier → position size, animated as you change a Conviction Tier. Lesson: sizing isn't a guess; it cascades from rules you earned.
- Strategy Backtest Equity Curve + Drawdown overlay — P/L line with drawdown under it; trade dots are entry+exit. Lesson: worst pain matters more than win rate.
- Annotated Institutional Event Card — one chart with text callouts at each event point + the one-paragraph Analyst Voice summary alongside. Lesson: many data streams → one coherent story. THE beginner viz.
M19 — Mobile Monitor (companion surface, not mobile-first)
Mobile Monitor (M19) — Separate thin client reading the same Bun backend + SQLite cache + adapter queue + LLM Gateway as the desktop workbench. For job A (passive monitoring: alerts, P/L glance, per-symbol story) NOT job B (active research: backtests, multi-panel cross-reference, screener, strategy authoring). Read-mostly; limited authoring (add to watchlist, ack alert, save post). Native is a later phase; v1 = Next responsive route, not React Native. Built second, after the desktop workbench.
Two-job split — Investor Flow serves two jobs at two home surfaces: (A) passive monitoring on the go = mobile phone, M19; (B) active research at a desk = desktop/laptop, M1–M18 workbench. One backend, two co-equal client surfaces. Going mobile-first would force job A's surface on job B (Robinhood's bet); the depth that justifies the product dies.into tabs. Build desktop workbench first; thin mobile companion second shares all backend work (refcounted shared cache means mobile is a second client, not a second product). ~30% more frontend, 0% more backend.
Two-Axis Investment Model (Fundamental × Technical)
Fundamental Thesis — The written WHY of an investment: what to own + invalidation criteria. Tracked over time (intact / weakening / broken). Broken thesis = exit regardless of TA. Process-grounded: Alfred's BKKT entry was 8 months of fundamental thesis-building before deployment.
Entry Confluence Rack — The accumulating WHEN: a structured checklist of technical confluences. Each lit = +1; more lit = better entry quality. Generalizes the old journal's confluenceCount into a structured, named, trackable rack. A user authors the Rack per-Strategy by pulling from the Confluence Library.
Two-Axis Posture — Every investment has two independent axes: fundamental conviction (Conviction Tier) × technical entry (Confluence Rack). High conviction + bad entry = WAIT (never "buy like an idiot"). The SizingEngine enforces the posture matrix as a hard guardrail. Override requires one-tap + written reason (mirrors Macro Regime Gate).
Entry Confluence Rack (4-factor default):
- A. Trend alignment — price > EMA200, EMA21 > EMA50, higher-highs/lows. +1 when all 3 hold.
- B. Pullback maturity — recent low near EMA21 or a key level + volume dryup on the dip. +1 when both.
- C. Catalyst proximity — earnings / 13D/A / insider informed-buy / rotation phase Accumulation→Expansion within ≤30 days. +1.
- D. Risk-bound coherence — Stop-loss set at a real level (under support / under EMA50) AND reward-to-risk ≥ 2:1. +1.
Entry regime labels (what the user sees, P6 plain English, P7 visual):
- 0–1 lit = "Bad entry" (WAIT — app shows which confluences are unlit + what would light them).
- 2 lit = "Marginal entry" (plan a small first deployment only).
- 3–4 lit = "Good entry" (DEPLOY per Conviction Tier capacity).
Confluence Slot — A single authored predicate lit(symbol, cachedData) → bool. Pulled from the Confluence Library. Rack = collection of slots. Default Rack = 4 slots (beginner); extendable to 6+ (advanced).
Confluence Library — Registry of named parameterizable slot templates: trendAlignment, pullbackToEMA21, catalystProximity, riskBoundCoherence, sma200w (200-week SMA proximity), rsImproving, breadthThrust, insiderInformed30d, instNetActivePositive, ivPercentileLow, etc. Long-horizon SMAs (e.g., 200-week) measure multi-year holder cost basis — strong thesis support, can also be authored as entry slots when price is within X% of the level.
Redundancy Awareness — When multiple lit slots measure the same underlying signal (e.g., 21-EMA > 50-EMA AND price > EMA200 AND price > 200-day SMA in a persistent uptrend), the app tags them "redundant — counts as one signal." Analyst Voice explains why; the user keeps/removes at will. Adding indicators doesn't necessarily improve entries; redundancy adds noise.
Default Rack by complexity — Beginner: 4-factor. Intermediate: +rotation-phase slot. Advanced: +full authorable Confluence Library (incl. 200-week SMA, VWAP, volume-profile POC, etc.).
Deployment Schedule — How capacity (Conviction-Tier-sized) is entered over time/space, gated by Confluence Rack accumulation. Replaces one-shot entry with corridor scale-in (Alfred's May 15-18 four-day deployment at a price corridor). Beginner-default: "deploy 40% at Rack≥3; deploy remaining 60% at Rack≥4 OR price revisits buy-zone / EMA21."
Thesis Monitoring (autonomous, 3 layers):
- L1 — Per-holding: every relevant event for that symbol cross-references the user's stated invalidation criteria; fires intact/weakening/broken per position.
- L2 — Portfolio posture: aggregated stock-level weakening ≥ threshold; correlation cluster over-exposure; regime change.
- L3 — Macro context: sector rotation phase flips; macro regime classification updates; market breadth thrust triggers. Each independently alertable; surfaces on M1 (L1), M10 portfolio dashboard (L2), M18 macro module (L3); M19 mobile shows all three in priority order. Not a forecast engine — never predicts returns; only translates stated invalidation criteria into alerts when matching events arrive. L1 runs via CacheRepository reads + LLM Gateway over local-only provider per ADR-0006 (thesis content = sensitive user data).
Risk Management (ADR-0007 territory)
RiskEngine (deep module) — Pure aggregator reading portfolio + theses (L1/L2/L3) + correlation clusters + macro regime → RiskPosture. Never touches network. Produces: totalRiskUsd/Pct, drawdownUsd/Pct, maxDrawdownTolerance status (healthy/warning/halted), clusterBreaches, regimeRisk (aligned/misaligned/hostile + recommendedCashPct), asymmetryScore, cashReservePct, recommendedActions (cut_to_cash/trim_cluster/halt_new_entries/close_thesis_broken/continue). Surfaces on M10 desktop + M19 mobile.
M20 Risk Posture — Single unified risk-picture surface. The "where you see your complete risk picture + what to cut" panel. Curated visualizations under P7 (drawdown gauge is NOT a decorative gauge — teaches the mechanic: drawdown-vs-tolerance-by-style-fit), recommendedActions with cited inputs. Prominent on M19 mobile.
The 6 risk primitives (locked dictionary)
Max Drawdown Tolerance — Per-user $/% threshold stated at onboarding. Hitting it → halt_new_entries (24h cooldown + L1 thesis re-evaluation). The Soros circuit breaker as user policy.
Total Risk Capacity — Max sum of open-position risk if all stops hit (beginner default 5% of equity). SizingEngine enforces per-position; RiskEngine enforces aggregate.
Correlation Cluster Cap — Max % equity in one factor driver (beginner default 25%; 40% intermediate). SizingEngine Layer 3 enforces at entry; RiskEngine surfaces breach across whole book.
Asymmetry Score — Portfolio-weighted reward-to-risk across open positions (stated target & stop). <1.0 → warning ("your trades are sized wrong on average — losing over time even winning some"). Soros's principle in a number; pedagogically powerful but confronting.
Regime Risk Posture — Portfolio-wide cash recommendation by regime-vs-style fit (aligned/misaligned/hostile). Trending-up → ≤20% cash; structurally-shifting → ≥50% cash + halt fresh entries if no thesis resists the new regime. Druckenmiller's "cut risk when regime turns before losses prove it."
Thesis-Broken Exit Rule — L1/L2/L3 thesis monitor flag = broken → push-alert (mobile) + popup (desktop) + immediate exit recommendation with cited event. One-tap-override-with-written-reason; never silently held. The Alfred discipline.
Default risk caps (by complexity)
- Beginner: total risk = 5% of equity; correlation cap = 25%; halt drawdown = -20%; cooldown = 24h.
- Intermediate: 10% / 40% / -30% / 12h.
- Advanced: authorable per user.
Derisking (the asymmetric-management discipline)
Derisking Strategy Library (6 strategies) — Derisking is asymmetric: winning trades have flexibility (scale out, trail, insure); losing trades have only cut (stop or thesis-broken). The app asks "winning or losing?" before suggesting a derisk path. Default derisk for A_STAR conviction winner = choice between scale-out (SRxTrades-style selling) and protect-with-collar (Spitznagel-style insuring).
- Scale-out at targets — trim 25%/target + breakeven-stop-after-first-trim + runners with trailing stop. SRxTrades mechanic.
- Stop-trail up — trail with EMA21 (swing) or EMA50 (trend); exit fully below EMA50 close. Sunil mechanic.
- Thesis-based partial derisk — trim by thesis-status: intact→hold full; weakening→trim 25-33%; broken→exit remaining. L1/L2/L3-driven. Alfred mechanic.
- Options-protected hold (collar) — buy protective put below current price (locks downside, keeps upside) + covered call above (collar) to fund the put. The Spitznagel/Universa convex path: don't sell, insure. For convicted winners where you want downside bounded but thesis intact.
- Regime-cut derisk — sell down to target cash posture (e.g. 50%); portfolio-wide not one-at-a-time. RiskEngine recommendedCashPct.
- Correlation-driven derisk — trim the cluster not the winner; reduce biggest cluster-cap violator.
Exit Strategy — First-class field on Strategy (alongside Setup). Previously we specified entries but not exits. Now: Exit Strategy = the derisk + exit logic authored per-Strategy, sourced from the Derisking Strategy Library (6 primitives above, default per complexity).
Two-Axis Derisking — The app considers BOTH thesis-status AND price-momentum before suggesting. Winning + thesis intact → collar or scale-out; winning + thesis weakening → scale-out + tighter •••; losing → cut only (never average down or sell puts against a loser; those are trader-paths beginners lose on, not in our app). LLM Analyst Voice distinguishes: "Thesis still intact at A-STAR? The collar protects downside while keeping your winners. Thesis weakening? Scale out — don't insure a name you might not want." +## Session Start Protocol (2026-07-08)
++Problem: The session-context-loader skill installed globally via npx skills add doesn't auto-trigger on OMP session start. Even though SKILL.md is in ~/.pi/agent/skills/session-start/SKILL.md, OMP only loads AGENTS.md files from project directories into context — it doesn't scan and invoke installed skills automatically.
++Investigation findings:
+- Confirmed skill file exists at both ~/.pi/agent/skills/session-start/SKILL.md (direct copy) and ~/.agents/skills/session-start/SKILL.md (symlink target)
+- Verified frontmatter format matches working skills like anti-sleep (name + description fields identical)
+- Discovered Pi/Claude Code only loads ~45 of 60+ installed skills into available tools list — session-start is in the "not loaded" group along with ~15 others
+- OMP's context injection mechanism: reads AGENTS.md from project directory and injects it into <context> block automatically. Skills are separate — they're available as tools but not auto-triggered.
++Resolution: Manual skill invocation instead of inlining session-start logic into project-specific AGENTS.md files (which wouldn't be generic anyway). Updated ~/Documents/investor-flow/AGENTS.md to remove conflicting instructions about reading wiki page directly, replaced with brief note that session-start skill handles context loading.
++Key takeaway: OMP loads AGENTS.md automatically for project-specific instructions, but does NOT auto-trigger skills from ~/.pi/agent/skills/ — requires manual invocation or explicit user request. npx skills add puts files in right place but doesn't change OMP's discovery behavior.