Describe a trade in plain English and IdiotProof turns it into a strategy that a 24/7 Monitor evaluates against live market data, firing only when its conditions, an LLM panel and a risk guard all agree. Everything it can do (every strategy, every trade, every Alpaca endpoint) is one idiotproof <noun> <verb> command away, run through a single local Hub, and the same commands are MCP tools so you can drive it by talking to Claude.
Try it: browse the public replay archive at mindattic.com/idiotproof/replays, where every published run shows the chart, the condition truth bands and each round-trip's P&L.
- Describe your edge the way you would say it out loud, for example "if NVDA pulls back to the 9 EMA in an uptrend with volume confirmation, go long with a 1% stop", and get a real, runnable strategy back.
- Stop watching charts at 4 AM. The Monitor inside the Hub re-reads every active strategy from SQL about once a second and acts on it while you sleep.
- No single point of failure on the money path: the strategy conditions, a multi-LLM voter quorum and the Risk Guardian must all agree before an order is placed.
- You cannot route real capital by accident. The Sandbox broker is the default, paper is the next step, and live trading needs an explicit per-strategy choice and a red confirmation modal.
- See exactly why something fired or did not: per-condition progress (
4/5 — waiting on OnReclaim(9)) is live in the UI, and every signal, veto and order lands in the audit log. - Your strategies stay yours: SQL-backed, owned per user, broker keys kept in MindAttic.Vault, full audit trail.
- Talk to it instead of clicking: the
idiotproofCLI and the matching MCP tools reach every service, so "show me INTC since yesterday's open" or "what's in my live account" becomes one command, and a Live order always stops for a confirmation code.
- Guided: a visual flowchart editor with one card per lifecycle phase.
- Script: write IdiotScript, the project's fluent C# DSL, directly.
- Describe: type plain English;
StrategyScriptGeneratorsends it to Claude throughMindAttic.Legion(a single call, on your own Claude key when you have saved one), with a verb catalog built by reflecting on the real DSL types so a model cannot invent syntax that does not compile, and the result is parse-checked.
Strategies are stored as canonical strict JSON. If a stored strategy cannot be read exactly, it is quarantined with a visible reason instead of being partially evaluated.
The flagship flow: buy a premarket gap at 4 AM ET, sell it before the 9:30 bell once momentum rolls over. Queue a ticker on the Gapper tab, pick a profile, dial in gap %, volume, price band, stops, peak-giveback and arm/sell-by times, and the Monitor handles the rest. You can also paste a video transcript and let GapperInterpreter extract candidate plays as review cards; nothing queues until you click.
- All strategy conditions match.
- The LLM voter quorum explicitly approves.
- The Risk Guardian clears stop presence, per-trade and daily loss limits, stop distance and account-risk percent.
Any gate can block the fire, and the reason goes to the audit log.
The Monitor can replay a strategy over a past day (replay, replay-all, scan) and render each run as a static HTML page: minute candles, VWAP, entry and exit markers, a per-condition truth band and a payoffs table. replay-regen rebuilds the archive from the ReplayRun rows in SQL, and it is published by hand to mindattic.com/idiotproof/replays.
- Research tab: a ranked feed of high-impact events from SEC filings, Alpaca news and Federal Register notices, scored 0 to 100 by the separate Research Scanner.
- Options tab: a manual options section with a full chain, per-strike breakeven, an intrinsic versus extrinsic ("real vs hype") meter and a plain-English order ticket. It is deliberately separate from the automated strategy pipeline.
- Learning Center: seeded articles with live-rendered strategy examples embedded through
[[...]]wikilinks. - Backtesting:
StrategyBacktesterand the gapper day replay run the same evaluation code as the live Monitor.
IdiotProof is CLI-first. One long-running process, the Hub (IdiotProof.Hub, loopback 127.0.0.1:5910), hosts the Monitor's trading loop, the single transaction gateway and every operation. Three front doors talk to it:
you ──► Claude ──► idiotproof (CLI) ─┐ ┌─► Blazor web app (same services)
└► idiotproof MCP ───┼── HTTP 127.0.0.1:5910 ─► IdiotProof.Hub
│ X-IdiotProof-Key ├─ Monitor loop (SQL leader lease, IP-LAW-10)
│ ├─ OperationDispatcher (reflected [Operation] catalog)
│ ├─ TransactionGateway: every order, every mode
│ │ owner → route → data → RiskGuardian → LLM → Live code → broker → audit
│ ├─ ConfirmationStore (Live codes), CommandLedger
│ └─ AlpacaApi (Trading + Market Data REST, per owner and mode)
- Every action is one class with an
[Operation("noun verb")]attribute inIdiotProof.Operations. The CLI flags, the MCP tool schema and the generated docs are all reflected from it, so they cannot drift apart. - All transactions go through the Hub. Market data, account reads and orders are fetched by the Hub with the owner's own keys; nothing calls Alpaca from the CLI or from Claude directly.
- Live money needs a code. Sandbox and Paper act immediately. A Live order, a Live strategy activation or any Live account change returns a preview and a single-use 6-character code (120 s, bound to the exact intent: change anything and the code is void);
idiotproof trade confirm <code>executes exactly what was previewed, once. The MCPtrade_confirmtool is deliberately not allow-listed, so Claude Code always asks you first. - Exit codes:
0ok,1failed,2usage,3blocked by a gate,4confirmation required. Every call is written to theCommandLedgerwith secrets redacted. Times are always US Eastern.
idiotproof hub start # start the Hub (hosts the Monitor; IDIOTPROOF_HUB_MONITOR=0 for API only)
idiotproof hub status
idiotproof users use <your-user-guid> # who commands act for (saved in %APPDATA%\MindAttic\IdiotProof\cli.json)
idiotproof account summary # paper + live equity, cash, buying power
idiotproof positions list # every open position, unrealized P&L
idiotproof market chart INTC # Braille candle chart: yesterday premarket to today after-hours
idiotproof strategy list --active
idiotproof trade buy NVDA 1 --mode paper
idiotproof trade buy NVDA 1 --mode live # exit 4: preview + code
idiotproof trade confirm K7Q2XM
idiotproof help # every command; `idiotproof help <noun>` for one familyHub autostart in Claude Code sessions. .claude/settings.json has a SessionStart hook (.claude/hooks/start-hub.ps1) that starts the Hub when nothing answers on 127.0.0.1:5910, so tools work from the first prompt. It never touches a Hub that is already running, and the Hub it starts has the Monitor off (IDIOTPROOF_HUB_MONITOR=0): opening a session never starts unattended trading. Set IDIOTPROOF_HUB_AUTOSTART_MONITOR=1 in the environment Claude Code launches from to have it start the trading Monitor too; to switch an already-started Hub, idiotproof hub stop then idiotproof hub start from a shell with the variable you want.
idiotproof resolves to idiotproof.cmd (or the Git Bash idiotproof script) at the repo root: the published CLI if present, else the Debug build, else dotnet run. MCP is registered in .mcp.json; it also serves the BIBLE, the user stories, the agent protocol and the live catalog as resources. A fresh agent session runs idiotproof agent bootstrap (it works with the Hub down) and follows docs/agent/IDIOTPROOF_PROTOCOL.md. idiotproof agent export regenerates the references from the catalog: docs/CLI_COMMANDS.md, docs/MCP_TOOLS.md and docs/agent/operation-catalog.json (agent export --check exits 1 when one is stale).
The project explains itself through the same door: idiotproof docs search <words> ranks hits across the BIBLE, stories, tickets, every library's XML doc comments and the static catalogs; docs law 3, docs section 4, docs story K1, docs ticket IP-T259, docs explain TransactionGateway (doc comment, path:line, callers) and docs dsl StopLoss open one entry.
market chart draws with MindAttic.Helpers.Ascii.AsciiCandleChart: each character is a 2x4 Braille dot cell (two time slots by four price levels), sessions sit side by side with the overnight gap removed, prior closes are reference lines, volume uses eighth-height blocks, and every line is exactly the requested width.
95.00 | :⡀ ⡀ : :
| ⣧⣀⣠⣤⣀⣀⣤⣤⡶⡶⡤⡦⡞⡶:⣷⣿⡻⡾⡛⡙⣻⣤⣤⣤⣄⡀⡀⡀⡀⣠⡄:⣄⡀⡀⡀⡀⡀⡀⡀⡀⡀:⡀⡀⡀⡀ <- Thu close 92.08
90.00 | ⠁⠉⠉⠈⠉⠉⠁ :⠈⠁ ⠈⠛⠛⠛⠋⠉ :⣿ :
| : :⣿⣼⡄ :
75.00 | : :⡇ ⠛⣧⣶⣆ :
| : :⠇ ⠁⠙⣧⣠⣤:⣦
+-+----+------+-++-----+------+---++----+----++---
4a 6a 9a 9:30a 12p 3p 4p 6p 4a
PRE REGULAR AFTER
The goal is that every Alpaca Trading API and Market Data API capability is reachable from the Hub. AlpacaApi (IdiotProof.Brokers) is the raw REST layer (both hosts, every verb, paging, 429 retry, typed errors) and AlpacaApiFactory (IdiotProof.Core/Trading) hands one out per owner and Paper/Live behind the same consent gate as order routing. On top of it:
| Family | Commands | Alpaca endpoints |
|---|---|---|
| Account | account summary, buying-power, pdt, activities, clock, calendar, config, config set, history |
/v2/account, /configurations, /activities, /portfolio/history, /v2/clock, /v2/calendar |
| Positions | positions list, show, reconcile, close, close-all, trade flatten |
/v2/positions |
| Orders | orders place, bracket/oco/oto, replace, cancel, cancel-all, list, show, history; trade buy/sell/short/cover/notional |
/v2/orders |
| Options | options level, chain, contracts, contract, quote, greeks, value, iv, bars, trades, latest, snapshot, positions, buy, sell, close, spread, exercise, do-not-exercise |
/v2/options/contracts, /v1beta1/options/*, exercise, mleg orders |
| Stocks data | market chart, quote, bars, trades, quotes, snapshot, latest, prev-close, auctions, conditions, exchanges, meta, movers, most-actives, news, fx, logo, scan-universe |
/v2/stocks/*, /v1beta1/screener, /v1beta1/news, /v1beta1/forex, /v1beta1/logos |
| Crypto | crypto bars, trades, quotes, latest, orderbook, snapshot, buy, sell, positions, wallets, transfers, whitelist, whitelist-add, whitelist-remove, fee-estimate, withdraw |
/v1beta3/crypto/us/*, /v2/wallets |
| Reference | assets list, assets show, assets bonds, market bond-prices, corporate-actions list/show, watchlists ... |
/v2/assets, fixed income, /v2/corporate_actions, /v1/corporate-actions, /v2/watchlists |
| Streams | orders stream, market watch, market stream status |
trade_updates websocket (paper/live), market data websockets (stocks sip/iex, crypto, options over msgpack, news) |
| Escape hatch | alpaca raw <GET|POST|PATCH|PUT|DELETE> <path> [--body] [--data] [--mode] |
any Trading API path or Market Data GET; order/position/exercise/wallet mutations refused, Live mutations need a code |
The endpoint-by-endpoint map, with the test that fails when an Alpaca endpoint has no command, is docs/ALPACA_COVERAGE.md. The Broker API (for broker-dealers running many customer accounts) is out of scope; OAuth Connect is already wired (AlpacaOAuthClient). alpaca raw reaches any endpoint before a typed command exists, but never the money path: mutations under /v2/orders, /v2/positions and /v2/wallets are refused with the gated command to use. The websockets run inside the Hub: the first orders stream for an account starts its trade_updates listener, which then records every fill and partial fill in the audit log and the trade diary until the Hub stops (whether or not anyone watches); market watch is a live tape; market stream status shows every socket's health. idiotproof help always shows what the running Hub has.
Long-running commands stream: the CLI calls them on the Hub's /api/stream (server-sent events) and prints each line as it happens until Ctrl+C (orders stream, market watch, monitor tail --follow, audit tail --follow, strategy progress --watch). MCP tools and --json get a bounded window instead (--seconds, 15 s by default, at most 300 s).
- Secrets live in MindAttic.Vault, not in the repo and not in the database (IP-T330): Alpaca keys in the
Brokersbucket (the machine owner'salpaca-paper/alpaca-live; every other user'sidiotproof-<userId>-alpaca-paper|live), each user's Claude key and Alpaca OAuth tokens inTokens(idiotproof-<userId>-claude,...-alpaca-oauth-access|refresh), host LLM keys inLLM, the Hub key (idiotproof-hub) inTokens. The owner isIdiotProof:VaultOwner, elseIDIOTPROOF_OWNER, elseHub:DefaultOwner, else the CLI's savedusers useowner. TheUserApiKeystable keeps only non-secret choices (routing opt-in, feed, model); nothing reads a secret from it. Legacy rows that still hold encrypted secrets are moved once withidiotproof keys migrate-to-vault(a dry run) andidiotproof keys migrate-to-vault --apply(copies what Vault lacks, verifies what both have, then clears the SQL columns; conflicts are reported and left alone). Account numbers are masked to their last four digits in the command ledger. - The Hub listens on loopback only (
127.0.0.1:5910) and every/api/opcall must carry theX-IdiotProof-Keyheader from Vault; nothing is exposed to the network. - Account data never reaches the docs. This repository and its documentation site are public: no account numbers, balances, positions or keys appear in the README,
docs/or the generated references (examples use placeholders), and a test scans them for key-shaped strings and account numbers. - An owner's Alpaca account is only touched after they opt in (
DefaultBroker = alpaca, IP-LAW-3); otherwise every order goes to the Sandbox simulator and account reads say so.
The system is one long-running process, the Hub, plus the optional web app, sharing one SQL database; the database is the communication channel. Anything you change from the CLI or the UI is picked up by the Monitor on its next pass, roughly a second later, with no restart. The full field-by-field walkthrough is in user-guide.htm, which the running app also serves at /user-guide.htm.
Prerequisites: .NET 10 SDK, SQL Server LocalDB (ships with Visual Studio or the SQL Server Express LocalDB package), and Node 18+ only if you want to run Cypress.
git clone https://github.com/mindattic/IdiotProof.git
cd IdiotProof
dotnet build IdiotProof.slnx
dotnet ef database update --project IdiotProof.Blazoridiotproof hub start # the Hub: hosts the Monitor loop and every operation
# Optional: the web UI (https://localhost:5001), same database and services
dotnet run --project IdiotProof.BlazorBoth default to the same LocalDB database (IdiotProof). The Hub acquires the SQL leader lease and starts ticking; idiotproof hub logs shows its log. idiotproof-monitor.exe (IdiotProof.Monitor) is now a shim that starts the Hub, so old shortcuts keep working; the lease guarantees only one process trades. Register an account in the UI on first run.
The Hub picks its data feed from the settings chain. Put Alpaca keys in the MindAttic broker keyring at %APPDATA%\MindAttic\Brokers\providers.json:
{ "alpaca-paper": { "type": "alpaca", "apiKey": "PK...", "secret": "..." } }Or set AlpacaApiKeyId and AlpacaApiSecretKey before starting the Hub. With keys present the Monitor uses Alpaca REST plus a live websocket stream (real-time SIP tape by default); without keys it falls back to a deterministic mock feed.
In the UI open the user menu, then API Keys. Enter your Alpaca key and secret, leave Paper checked, enable "Route my orders to this Alpaca account" and Save All. The Hub decrypts those keys through the shared ASP.NET Core Data Protection key ring and places your orders on your account. Without the toggle, orders go to the built-in Sandbox broker (simulated fills), the safe default by design. Live trading means unchecking Paper and confirming the red modal; each strategy's own BrokerMode (Paper, Live or Sandbox) then overrides the global flag for that strategy.
- From a transcript: paste a video transcript or any natural language into the Gapper tab's "From a transcript" box and press Interpret. Claude, through Legion and
GapperInterpreter, extracts the gap plays as reviewable candidate cards with inferred dial-ins. Queue the ones you like, or Load into dials to tweak first. - By hand: enter a ticker, pick a profile from
IdiotProof.Core/Catalogs/gapper-profiles.json, dial in gap %, volume, price band, SL/TSL, peak-giveback and arm/sell-by times, then Queue Gapper.
From then on it is hands-off. The Monitor screens the ticker every pass in the 4:00 to 9:00 AM ET premarket window, buys through the three gates when the criteria hit (premarket orders are limit plus extended-hours automatically), shows HOLDING qty @ price live, and sells before the 9:30 bell: momentum giveback first, hard sell-by as the backstop.
The Hub window narrates every decision (idiotproof hub logs, idiotproof monitor tail); the Gapper and Strategies tabs mirror it live. To rehearse without keys or money, start the Hub with IDIOTPROOF_FEED=mock, queue a ticker starting with GAP (for example GAPT, for which the mock feed fabricates a gap day) and leave routing off. The session gate still runs on the real ET clock, so entries only fire during actual premarket hours, Monday to Friday. The Monitor also refuses to place a real order against mock market data even if a broker is configured.
Data flows one way from market data down to an order; audit and state flow back up through SQL to every UI that is watching.
┌───────────────────────────────┐
│ Trader (browser) │
└───────────────┬─────────────────┘
│
┌─────────────────▼──────────────────┐ ┌─────────────────────┐
│ IdiotProof.Blazor │◄─────┤ Cypress E2E │
│ Strategies · Strategy Builder │ │ (9 specs, tests/) │
│ (Guided / Script / Describe) │ └─────────────────────┘
│ Gapper · Learn · Research │
│ Options · Backtest · API Keys │
└─┬──────────┬─────────────┬──────────┘
│ │ │
┌────────▼──┐ ┌─────▼─────┐ ┌─────▼───────────┐
│StrategyBu-│ │ Wikilink │ │StrategyScript- │
│ilderRend- │ │ Parser │ │Generator │──► MindAttic.Legion
│erer │ └───────────┘ │(reflects verb │ (voter panel, legion.json)
└─┬─────────┘ │catalog, sends │───►┌──────────────────┐
│ │to Claude/GPT/ │ │ %APPDATA%\ │
│ │Gemini/DeepSeek)│ │ MindAttic\LLM\ │
│ └────────────────┘ │ providers.json │
│ └──────────────────┘
┌──────────▼──────────────────────────┐
│ IdiotProof.Engine │
│ AppSettings (disk→env→Vault→config) │
│ ServiceRegistration · SupervisedLoop │
│ AuditLogger · WorkspaceManager │
└─┬───────────┬───────────┬─────────────┘
│ │ │
┌─────▼───┐ ┌────▼─────┐ ┌───▼─────────────────┐
│ Models │ │Indicators│ │ Scripting │
│ (nouns) │ │ RSI EMA │ │ IdiotScript DSL, │
│ │ │ ATR MACD │ │ StrategyBuilder, │
│ │ │ VWAP ... │ │ ScriptParser, │
│ │ │ │ │ GapperProfile, │
│ │ │ │ │ TradingSchedule (ET) │
└────┬────┘ └────┬─────┘ └──────────┬────────────┘
│ │ │
┌────▼────────────▼───────┐ ┌───────▼─────────────┐
│ Shared │ │ Strategies │
│ RiskGuardian (final │ │ IStrategy·DslStr- │
│ veto) · IndicatorSnap │ │ ategy·IndicatorSnap- │
└────┬─────────────────── ┘ │ shotBuilder·Gapper- │
│ │ ExitEvaluator· │
│ │ StrategyBacktester │
│ └──────────┬────────────┘
│ │
┌────▼──────────────┐ ┌─────────▼────────────┐
│ DataFeeds │ │ Brokers │
│ IMarketDataFeed: │ │ IBrokerClient: │
│ AlpacaDataFeed │ │ AlpacaBrokerClient │
│ (REST, sip/iex) │ │ SandboxBrokerClient │
│ AlpacaStreamingCl- │ │ BrokerRouter │
│ ient (websocket) │ │ (Sandbox is always │
│ MockDataFeed │ │ the safe default) │
│ SwitchableFeed │ └────────────────────────┘
└────────┬───────────┘
│
┌────────▼──────────────────────────────────────────┐
│ SQL Server (LocalDB) │
│ IdiotProof database │
│ Strategies · UserPreferences · ConditionProgress │
│ UserApiKeys · AuditLogs · Workspaces · LiveBars │
│ ResearchClaim · TrackedTicker · TradeDiaryEntry ... │
└────────▲───────────────────────────────────┬─────────┘
│ │
┌──────────┴───────────┐ ┌─────────▼──────────────┐
│ Monitor in the Hub │ │ IdiotProof.ResearchSc- │
│ (IdiotProof.Hub, 24/7)│ │ anner (one-shot, │
│ evaluates → 3 gates │ │ Scheduled-Task-fired) │
│ → order → exit mgmt │ └─────────────────────────┘
└────────────────────────┘
IdiotProof is one solution with several front doors and one shared engine:
IdiotProof.Blazoris a Blazor Server web app where a trader authors strategies, watches them live, and manages accounts, keys and research.IdiotProof.Hubis the one long-running process: its Monitor runs unattended, loads every active strategy from SQL, evaluates it continuously, walks the three gates, places orders through the transaction gateway and manages open positions to their exit; it also serves everyidiotproofcommand and MCP tool.IdiotProof.ResearchScanneris a one-shot console, meant for a Scheduled Task rather than a daemon, that sweeps EDGAR filings, Alpaca news and Federal Register notices and writes scored events to the same database for the/researchtab.
Everything shares one SQL database as the single source of truth, one settings and credential overlay chain (IdiotProof.Engine.Settings.AppSettings), and the same domain libraries, so a strategy is evaluated identically everywhere.
Reading the diagram:
- A trader authors a strategy in
IdiotProof.Blazor. It is saved to theStrategiestable as canonical strict JSON (ScriptJson), with IdiotScript text (ScriptText) kept only as a human-readable view; see IP-LAW-8. IdiotProof.Monitorre-reads everyIsActive = truerow on every pass, builds anIndicatorSnapshotper symbol from live candles (IdiotProof.Indicatorsmath overIdiotProof.DataFeedsdata), and walks each strategy's entry conditions.- A full pass on all conditions is a candidate signal, not an order. It must still clear the LLM voter panel and the
RiskGuardian(IdiotProof.Shared.Risk) beforeIdiotProof.Brokersplaces anything. - Every step (condition progress, votes, vetoes, fills, exits) is written back to SQL (
ConditionProgress,AuditLogs,LiveBar), which the Blazor UI polls for live badges without any direct connection to the Monitor. IdiotProof.ResearchScanneris architecturally separate: it never touches strategies or orders, it only populatesResearchClaimandTrackedTickerrows that the/researchpage reads.
It is Alpaca-only in the active build. A new broker plugs in by implementing IBrokerClient and registering it with BrokerRouter.
Every project below is registered in IdiotProof.slnx.
| Project | Type | Responsibility | Key types |
|---|---|---|---|
IdiotProof.Models |
Class library | Domain DTOs and enums, the nouns everything else shares. | Candle, TradeSignal, TradeSetup, OrderRequest/OrderResult, Position, TradeDirection, TradingSession, BrokerType {Alpaca, Sandbox}, StrategyType; options: AssetClass {Equity, Option}, OptionRight, OptionContract (OCC parse/build), OptionQuote, OptionGreeks |
IdiotProof.Shared |
Class library | Cross-cutting primitives used by almost every other project. | RiskGuardian (+ RiskGuardianConfig/Result), the final pre-trade veto; IndicatorSnapshot; LogMessage; SettingsMetadata; Branding (console banner); Options/: IntrinsicValueCalculator (real vs hype split, breakeven, DTE), BlackScholesCalculator (theoretical price + implied-vol solver), SellSignalEvaluator (informational "consider taking profit") |
IdiotProof.Indicators |
Class library | Pure indicator math, no I/O. | ADX, ATR, BollingerBands, CCI, EMA, MACD, Momentum, OBV, RSI, SMA, Stochastic, VWAP, WilliamsR, CandlestickPatterns |
IdiotProof.Scripting |
Class library | The IdiotScript DSL: authoring, parsing, serializing, scheduling. | IdiotScript/StrategyBuilder/Conditions (fluent authoring), ScriptParser (tolerant text to model), StrategyJson (canonical strict-JSON codec), StrategyLoader (fail-closed load), StrategyHtml (render), GapperProfile (dialable template), EmaPeriodCollector, TradingSchedule/MarketTime (ET session clock) |
IdiotProof.Strategies |
Class library | Turns a parsed definition into something evaluatable and testable. | IStrategy, DslStrategy (adapter), IndicatorSnapshotBuilder, StrategyBranchResolver (If/ElseIf/Else), GapperExitEvaluator (sell-by/stop/target/peak-giveback), GapperDayBacktester, Backtesting/StrategyBacktester + BacktestReport |
IdiotProof.DataFeeds |
Class library | Market data abstraction and providers. | IMarketDataFeed (+ default GetPreviousCloseAsync), AlpacaDataFeed (REST, sip/iex), AlpacaStreamingClient (websocket trades + minute bars), MockDataFeed (deterministic gap simulation), SwitchableMarketDataFeed |
IdiotProof.Brokers |
Class library | Order routing abstraction and providers. | IBrokerClient (equity members + default-implemented options members: SupportsOptions, GetOptionTradingLevelAsync, GetOptionChainAsync, GetOptionQuotesAsync), AlpacaBrokerClient (orders, positions, account, option contracts, option snapshots, single-leg option orders), AlpacaOAuthClient, SandboxBrokerClient (in-memory fills + a synthetic options chain), BrokerRouter (Sandbox always registered as the safe fallback) |
IdiotProof.Engine |
Class library | The DI root shared by every host. | ServiceRegistration.AddIdiotProofEngine(...), Settings/AppSettings (disk, env, MindAttic keyrings, IConfiguration overlay chain), Storage/IStorageProvider/StorageLocation, SupervisedLoop (fault-tolerant tick loop with backoff + heartbeat), AuditLogger, Workspace/WorkspaceManager + JsonFileWorkspaceStore (legacy JSON path; the Blazor host swaps in a SQL-backed store) |
IdiotProof.Core |
Class library | The one DI graph every host shares: AppDbContext, entities, EF migrations, the non-UI services, identity seams, the transaction gateway and Alpaca access. |
AddIdiotProofCore/AddIdiotProofTrading/AddIdiotProofMonitor, TransactionGateway, OrderIntent, ConfirmationStore, AlpacaApiFactory, CommandLedgerRepository, SecretRedactor, OwnerResolver, Monitoring/MonitorWorker |
IdiotProof.Operations |
Class library | Every action as an [Operation("noun verb")] class; the catalog the CLI, MCP and docs reflect from. |
OperationCatalog, OperationDispatcher, ArgsBinder, OperationResult; one folder per noun (Account/, Orders/, Options/, Market/, Crypto/, Strategies/, Gapper/, Replay/, ...) and Docs/ (KnowledgeIndex, docs *) |
IdiotProof.Hub |
ASP.NET Core minimal API, loopback | The one long-running process: hosts the Monitor loop, the gateway, the Alpaca websockets and /api/op, /api/stream, /api/catalog, /api/health. |
Program.cs, HubEndpoints |
IdiotProof.Cli |
Console (idiotproof) |
Thin client: parses noun verb --flags, POSTs to the Hub, prints tables or --json; hub start/stop/status/logs, users use, agent bootstrap, agent export. |
CliApp, HubClient, HelpRenderer, CommandDocs, AgentExport |
IdiotProof.Mcp |
stdio MCP server (idiotproof-mcp) |
One MCP tool per operation, forwarded to the Hub; the BIBLE, stories, protocol and catalog as resources. | ToolSpec, HubTool, HubInvoker, ToolDocs, DocResources |
IdiotProof.Blazor |
ASP.NET Core Blazor Server app | The web front door: strategy authoring and monitoring, accounts, keys, research, learning. | See The web app |
IdiotProof.UI |
Razor Class Library | Shared component library referenced by IdiotProof.Blazor. Presentational only. |
Components/Options/: OptionsChainView, OptionOrderTicket, OptionPositionTracker, OptionsLiveElevationModal, OptionsPresenter + view models; Components/Shared/: Tooltip, Term; wwwroot/css/options.css, tooltip CSS and JS |
IdiotProof.Settings |
Razor Class Library | IdiotProof's settings tab as a portable component (no route, render mode or auth lookup; the host passes UserId), so MindAttic.Vault.Desktop/Web can host it unmodified (IP-T335). /settings in IdiotProof.Blazor is now a thin host for it. |
IdiotProofSettings.razor (Risk Guardian limits, theme) |
IdiotProof.Monitor |
Console (idiotproof-monitor) |
Backward-compatible shim: starts the Hub, which hosts the Monitor; operator subcommands forward to Hub operations. The loop itself (MonitorWorker, MonitorLeaderLease, scanners, replay and dataset tooling) lives in IdiotProof.Core/Monitoring. |
HubShim, MonitorForwarding, MonitorCli |
IdiotProof.ResearchScanner |
One-shot console | Autonomous market-event research sweep; not a daemon, not part of the trading loop. | Program.cs, ScanPassRunner |
IdiotProof.Engine.Tests |
NUnit | RiskGuardian gate, SupervisedLoop resilience, WorkspaceManager, options pricing math (OCC, intrinsic/extrinsic, Black-Scholes, IV round-trips, sell signal). | — |
IdiotProof.Indicators.Tests |
NUnit | RSI/EMA/ATR/MACD/VWAP math + ADX Wilder-seed regression. | — |
IdiotProof.Strategies.Tests |
NUnit | DSL round-trip, backtester, gapper lifecycle, canonical-JSON contract, and a large family of exhaustive combinatorial matrix tests. | — |
IdiotProof.Brokers.Tests |
NUnit | BrokerRouter Sandbox default and safe fallback, Sandbox fill simulation, Sandbox synthetic options chain, Alpaca options and equity-short wire format against canned responses; opt-in real-paper options tests. | — |
IdiotProof.Blazor.Tests |
NUnit | StrategyScriptGenerator verb-catalog reflection, the LLM gate on Legion's voter panel (LlmVotingServiceTests) and per-owner Claude keys and their isolation (UserClaudeKeyResolverTests, ClaudeKeyIsolationTests, UserClaudeKeyRoutingTests), the password-reset flow (PasswordResetFlowTests), research-pipeline services, repository guard rails. |
— |
IdiotProof.Monitor.Tests |
NUnit | Long and short order shapes on the Sandbox broker (DirectionalOrdersTests), PremarketFadeScanner and BeBexDecayScanner math; opt-in real-paper short and sell_to_close probes (AlpacaPaperShortIntegrationTests). |
— |
IdiotProof.UI.Tests |
NUnit | Options presenter, option position view and options glossary. | — |
tests/IdiotProof.Cypress |
Cypress 13 | End-to-end Blazor UI tests (9 specs). | — |
IdiotProof.Core and IdiotProof.Cli were rebuilt in October 2026 as the shared service graph and the Hub's command line; they are unrelated to the trees of the same names that docs/BIBLE.md section 3 records as deleted on 2026-06-07. Test projects for the new layers: IdiotProof.Core.Tests, IdiotProof.Operations.Tests, IdiotProof.Cli.Tests, IdiotProof.Mcp.Tests.
Nouns (IdiotProof.Models, IdiotProof.Shared):
Candle: one OHLCV bar.TradeSignal: the output ofIStrategy.Evaluate, a candidate to fire.TradeSetupandRiskLimits: decimal-priced inputs thatRiskGuardianvalidates.OrderRequest,OrderResult,Position: the broker-facing order lifecycle.StrategyDefinition(IdiotProof.Scripting): a parsed strategy of phases, conditions and branches.- Enums in
IdiotProof.Models/Enums.cs:TradeDirection {Long, Short},TradingSession {Premarket, RTH, AfterHours, Extended},OrderType,OrderSide,PriceType,ConfidenceGrade,BrokerType {Alpaca, Sandbox},StrategyType {Iti, BreakoutPullback, LowHigh, FluentDsl, Custom},WorkspaceState.
Verbs (the key services, by call site):
Stock.Ticker(symbol)returns aStrategyBuilder(IdiotProof.Scripting), the entry point for authoring IdiotScript.ScriptParser.Parse(...),StrategyJson.SerializeandDeserializeconvert between text, JSON and the object model.IStrategy.Evaluate(symbol, candles, context)returnsIReadOnlyList<TradeSignal>.DslStrategyadapts a parsedStrategyDefinitioninto anIStrategy.IndicatorSnapshotBuilder.Build(...)and.BuildWithEmas(...)produce theIndicatorSnapshotevery condition evaluates against.StrategyBranchResolver.Resolve(def, snapshot)appliesIf/Then/ElseIf/Elseoverrides before the entry conditions are read.StrategyBacktester.Run(...)returns aBacktestReport;GapperDayBacktesteris the gapper-specific day-replay variant.RiskGuardian.ValidateTrade(setup)returns a verdict withIsApprovedandBlockReasons;RecordTradePnL(realized)feeds the daily circuit breaker.SupervisedLoop.RunAsync(options, ct)is the fault-tolerant tick loop every long-running host (currently just the Monitor) runs under.IBrokerClient.PlaceOrderAsync(...)is called viaBrokerRouter.PlaceOrderAsync(...); Sandbox is the always-registered fallback.GapperExitEvaluator.Evaluate(...)and.EvaluateShort(...)give a pure, clock-parameterized sell-by, stop, take-profit or peak-giveback verdict for a held position.IMarketDataFeed.GetHistoricalCandlesAsync(...),.GetLatestPriceAsync(...)and.GetPreviousCloseAsync(...)are served by Alpaca (REST + websocket) or Mock, selected bySwitchableMarketDataFeed.- Research subsystem (
IdiotProof.Blazor/Services, driven byIdiotProof.ResearchScanner):TickerUniverseService,EdgarService,Form4Parser,CorporateActionDetector,RegulatoryScanner,CatalystExtractor,OutcomeBackfillService,SignificanceScorer,ResearchService; see BIBLE §4.4.
%LOCALAPPDATA%\MindAttic\IdiotProof\ ← per-app state
└── Settings\app-settings.json (legacy disk overlay; SQL is canonical for runtime state)
%APPDATA%\MindAttic\ ← shared keyrings (shared across the MindAttic family)
├── LLM\providers.json (Claude/OpenAI/Gemini/DeepSeek keys — MindAttic.Legion's home)
├── Brokers\providers.json (alpaca-paper, alpaca-live — IdiotProof's own bucket)
└── Security\providers.json (pepper.v1, bootstrap-token — MindAttic.Authentication)
SQL Server (LocalDB by default) ← canonical runtime state, shared by Blazor + Monitor + ResearchScanner
└── IdiotProof database
├── AuthUsers, ... (MindAttic.Authentication — Argon2id, sessions, MFA scaffolding)
├── UserApiKeys (per-user routing opt-in, feed, Claude model; secrets live in Vault)
├── Strategies (UUIDv7 id, OwnerUserId, Title, ScriptJson canonical, ScriptText view, IsActive, BrokerMode, PositionQty, ...)
├── UserPreferences (Theme, ActiveAccountId, OpenStrategyTabs, RiskGuardian config, UiStateJson)
├── LearningArticles (Slug, Category, Title, BodyMarkdown, Order — seeded by LearningContentSeeder)
├── SettingsKv (generic KV store — currently unconsumed)
├── Workspaces (per-user containers — Watchlist + Strategies + risk params, schema-tolerant BodyJson)
├── AuditLogs (append-only: signal fires, orders, broker switches, risk vetoes, monitor start/stop)
├── ConditionProgress (one row per Strategy — Monitor's most recent N/M evaluation snapshot)
├── LiveBar (per-strategy per-tick OHLCV + condition bits, feeds any live chart)
├── TradeDiaryEntry (operator-facing trade journal)
├── ResearchClaim, TrackedTicker,
│ InsiderTransaction, ... (ResearchScanner output tables)
└── ReplayRun, ReplayFeatureRows, ... (offline strategy replay + ML feature store)
Connection string priority, identical for the Blazor host, the Monitor and the ResearchScanner:
- The
ConnectionStrings__IdiotProofenvironment variable. ConnectionStrings:IdiotProoffromIConfiguration(appsettings.json).- The LocalDB fallback:
Server=(localdb)\MSSQLLocalDB;Database=IdiotProof;Trusted_Connection=True;TrustServerCertificate=True;.
IDIOTPROOF_DATA_DIR overrides the per-app state root, which is useful for parallel test and dev runs.
Settings overlay chain (IdiotProof.Engine.Settings.AppSettings, applied by every host in the same order, later wins): disk, environment variables, MindAttic LLM keyring, MindAttic broker keyring, then IConfiguration (User Secrets, App Service application settings or Azure Key Vault).
Credentials:
- Claude and other LLM keys for the host:
%APPDATA%\MindAttic\LLM\providers.json(the canonical MindAttic keyring) or configuration; that is the only way to change the host key. A Claude key pasted into the API Keys page is stored in that user's own Vault Tokens entry only and is used for that user's own requests: the Monitor's LLM gate on their strategies, the Describe tab, the Gapper transcript reader and Research analysis (IClaudeKeyContext: their key, then the host key, else a "no key configured" message). The scheduled research scan uses the host key. - Alpaca keys:
%APPDATA%\MindAttic\Brokers\providers.jsonwithalpaca-paperandalpaca-liveentries (the owner's), plusidiotproof-<userId>-alpaca-paper|livefor any other user. Set them withidiotproof keys set alpaca-paper|alpaca-liveor the API Keys page; never in SQL. - A Development-only
.envat%APPDATA%\MindAttic\IdiotProof\.envprefillsDEV_USERNAMEandDEV_PASSWORDon the Login page; it is never loaded outsideDevelopment. See.env.example.
Every strategy walks through fixed phases. The visual builder renders one card per phase, and the parser rejects verbs used in the wrong phase.
| # | Phase | What it answers | Example verbs |
|---|---|---|---|
| 1 | Setup | Ticker, session, window | Stock.Ticker, Session, Quantity |
| 2 | Filters | Regime gates (always on) | RequireAdxAbove, RequireEmaStack |
| 3 | Entry | Triggers (AND of conditions) | IsAboveVwap, OnReclaim, IsBullishEngulfing |
| 4 | Order | Direction and size | Long, Short, Quantity |
| 5 | Risk | Stop placement | StopLoss, TrailingStopLoss |
| 6 | Exit | Targets, time exit | TakeProfit, ExitStrategy |
This catalog is illustrative, drawn from the fluent builder and the Learning Center seed content. IP-LAW-4 requires StrategyScriptGenerator to build its LLM prompt by reflecting on the real StrategyBuilder and Conditions types, so the ground-truth catalog is whatever compiles in IdiotProof.Scripting today. Treat this as a tour, not a spec.
Setup:
Stock.Ticker(symbol): required first call..Session(TradingSession):Premarket,RTH,AfterHoursorExtended..Quantity(int): share count.
Filters (regime gates):
.RequireAdxAbove(threshold = 20): trending market only..RequireEmaStack(fast, slow): fast EMA above slow confirms an uptrend.
Entry, VWAP:
.IsAboveVwap()and.IsBelowVwap()(aliasesAboveVwap,BelowVwap)..OnVwapReclaim()and.OnVwapLoss().
Entry, EMA family:
.IsAboveEma(period)and.IsBelowEma(period)..IsBetweenEma(fast, slow): the pullback zone..OnReclaim(period): prior bar at or below the N-EMA, current bar back above.
Entry, RSI, MACD, ADX and DI:
.IsRsiOversold(threshold = 30)and.IsRsiOverbought(threshold = 70)..IsRsiBullishDivergence()and.IsRsiBearishDivergence()..IsMacdBullish()and.IsMacdBearish()..IsAdxAbove(threshold),.IsDiPositive(),.IsDiNegative().
Entry, volume, gap and levels:
.WithVolumeConfirm(multiplier = 1.2),.IsVolumeAbove(multiplier),.VolumeSpike(multiplier = 2.0)..IsGapUp(minPercent = 3)and.IsGapDown(minPercent = 3)..IsAtSupport(tolerancePercent = 0.5)and.IsAtResistance(tolerancePercent = 0.5)..HoldsAbove(price),.HoldsBelow(price),.IsNear(price, tolerance),.BreaksAbove(price),.BreaksBelow(price).
Entry, candlestick patterns:
.IsBullishEngulfing()and.IsBearishEngulfing()..IsHammer()and.IsShootingStar()..IsDoji().
Order, risk and exit:
.Long(),.Short(),.Order(TradeDirection)..Quantity(int shares)or.Quantity(decimal dollars): share count or notional dollars (Alpaca'snotionalfield). Setting one clears the other..StopLoss(price),.StopLossPercent(percent),.TrailingStopLoss(percent)..TakeProfit(price),.TakeProfit(t1, t2, t3?),.TakeProfitPercent(percent)..ExitStrategy(timeOfDay).
using static IdiotProof.Scripting.Conditions;
Stock.Ticker("SPY")
.RequireAdxAbove(20)
.If(IsAboveVwap.And(IsEmaAbove(9)))
.Then(b => b.Long().StopLossPercent(1).TakeProfitPercent(2))
.ElseIf(c => c.IsBelowVwap().IsEmaBelow(9),
b => b.Short().StopLossPercent(1).TakeProfitPercent(2))
.Else(b => b.Long().TakeProfitPercent(0.5))
.Build();Conditions compose with .And(), .Or() and .Not(). Branch blocks evaluate top-down, and the first match's overrides apply on top of the base strategy. They are resolved at evaluation time by StrategyBranchResolver.Resolve(def, snapshot), which the Monitor calls on every pass, so branches work identically live and in the backtester.
Stock.Ticker("NVDA")
.RequireAdxAbove(20) // regime gate: trending market
.RequireEmaStack(9, 31) // confirm uptrend (9 above 31)
.IsAboveVwap() // institutional bullish bias
.IsBetweenEma(9, 31) // price in pullback zone
.OnReclaim(9) // trigger: closed back above 9
.WithVolumeConfirm(1.2) // 1.2x avg volume on trigger bar
.Long()
.StopLoss(450) // below the 31 EMA at signal time
.TakeProfit(485) // ~2x risk
.Build();The semantic model (StrategyDefinition), serialized as versioned strict JSON (Strategy.ScriptJson, via IdiotProof.Scripting/StrategyJson.cs), is what every evaluator runs. Reads fail closed: an unknown schema version, condition type or property throws StrategyJsonException and the strategy is quarantined with a visible reason in ConditionProgress (see the EvaluateOneAsync handling in MonitorWorker.cs), never partially evaluated. IdiotScript text (ScriptText) is a generated human view plus the tolerant input path for hand-typed and legacy rows, never the money-path source of truth. StrategyLoader.Load(json, text) implements the canonical-first, tolerant-text-fallback contract (IP-LAW-8).
The Monitor is the unattended evaluator and executor (IP-LAW-10). Its code lives in IdiotProof.Core/Monitoring and it runs inside the Hub (idiotproof hub start; IDIOTPROOF_HUB_MONITOR=0 serves operations without it). It is a BackgroundService (MonitorWorker) running under SupervisedLoop so a bad tick backs off and retries instead of crashing the process (IP-LAW-5). Every order it places goes through the TransactionGateway (IP-LAW-12). idiotproof monitor status|pause|resume|tick-once|tail inspects and steers it.
idiotproof hub start
idiotproof monitor statusVerified against MonitorWorker.TickAsync, EvaluateOneAsync and FireAsync:
- Trading-schedule gate:
TradingSchedule.Classify(nowUtc)classifies the moment asHibernateor an active window. Outside active hours the loop only emits a liveness ping every 5 minutes. - Re-read active strategies: every
IsActive = truerow, grouped by symbol. UI edits (queue, toggle, dial-in change) apply on the next pass with no restart. - Fetch candles: a rolling 240-minute-bar cache per symbol (Alpaca REST, refreshed every 5 minutes, or every 30 seconds if the last fetch came back empty), topped up between REST refreshes by the Alpaca websocket stream (
AlpacaStreamingClient) when keys are present, plus the previous daily close, cached per ET day, for gap math. - Per strategy: load the canonical
ScriptJson(fail-closed quarantine on rejection, escalated loudly if the strategy is holding a position, because quarantine also stops its exit rules), resolveIf/ElseIf/Elsebranches against the indicator snapshot, then either manage an open position's exit or walk entry conditions one by one. - Per-condition progress is upserted to
ConditionProgressevery pass (PassedCount,TotalCount,FirstFailingVerb), which the Strategies page polls for its live badges, and a throttledLiveBarrow is written for chart consumers. - On a full pass, two more gates run before any order (IP-LAW-1). Both must pass before
RecordFiredAsyncbumpsLastFiredUtcandFireCount:LlmVotingService, on MindAttic.Legion's own voter panel: everylegion.jsonvoter (claude, openai, gemini, deepseek) that has a key casts an Approve, Reject or Abstain ballot through a rotating trading lens (Risk Manager, Momentum Trader, Technical Analyst). The Claude voter runs on the strategy owner's own key from the API Keys page, falling back to the host key (UserClaudeKeyResolver); the other voters use the shared MindAttic LLM keyring. Approve needsLlmConsensusThreshold(default 66%) of the counted votes; zero votes, unparseable ballots, a reject or a below-threshold split all fail closed. The gate is skipped only when voting is off for both the host and the owner, or no Claude key resolves.RiskGuardian(IdiotProof.Shared.Risk, the final pre-trade veto, IP-LAW-2), a per-user instance cached byRiskGuardianServiceso the in-memory daily-loss circuit breaker survives across signals. It validates stop-loss presence and side, per-trade and daily loss limits, stop-distance bounds and account-risk percent.
- Placing the order:
UserBrokerResolverresolves the strategy's ownBrokerMode(Paper, Live or Sandbox), independent of any other strategy. Premarket and after-hours orders go in as limit plusextended_hours(an Alpaca requirement); regular-hours entries go in as a marketable limit (entry price + 0.2%) so a thin book cannot fill far off the evaluated price. A short entry is asell_to_openlimit at entry price - 0.2% (DirectionalOrders). As a hard interlock, the Monitor refuses to place a non-Sandbox order while the market-data feed is Mock. - Managing the open position every pass via
GapperExitEvaluator.Evaluateand.EvaluateShort: hard sell-by time, hard and trailing stops, take-profit, and the peak-giveback momentum rollover. Realized P&L feeds back into the daily circuit breaker. A long exits with asell_to_closelimit at -0.5%, a short covers with abuy_to_closelimit at +0.5%; before either, the Monitor reconciles against the broker position on the strategy's own side (a short is a negative broker quantity), and realized P&L is inverted for a short. Exit orders reduce risk, so they skip the LLM panel by design but are always audit-logged.
PremarketFadeScanner: a detection-only blow-off and fade alert, scanned every 5 minutes between 9:00 and 10:00 AM ET for every registered user. It never creates a strategy or places an order.AutoGapperScanner: resolved on demand by theauto-gapperoperator subcommand; it has no scheduled trigger of its own.- Daily audit-log pruning: every 24 hours, keeping 30 days of history with a 2,000-row floor.
- Duplicate-fire guard: at most one gapper-type strategy per symbol may fire per pass, even if two active rows for the same symbol pass in the same tick.
| Env var | Default | Notes |
|---|---|---|
IDIOTPROOF_MONITOR_INTERVAL |
1s |
Evaluation cadence (30s, 5m, bare seconds). The websocket stream keeps prices fresh between REST refreshes regardless of this value. |
IDIOTPROOF_FEED |
auto | alpaca or mock; auto means Alpaca when keys resolve. |
IDIOTPROOF_BROKER |
sandbox |
alpaca opts the global account into real routing; per-strategy routing is BrokerMode and the API Keys toggle. |
IDIOTPROOF_ALPACA_FEED |
sip |
Data tier for REST and streaming. sip needs an Algo Trader Plus subscription and falls back on rejection; set iex explicitly for the free tier. |
IDIOTPROOF_STREAMING |
on | 0 disables the websocket stream (REST only). |
IDIOTPROOF_SELFPING |
30m |
Liveness line cadence; 0 disables it. |
IDIOTPROOF_PRINT_FILLS |
on | 0 silences the framed ENTRY and EXIT console blocks; the structured log line still fires. |
The old Monitor subcommands are Hub operations now; idiotproof-monitor <subcommand> forwards to them (see IdiotProof.Monitor/MonitorForwarding.cs), and --local keeps the in-process path for replay and scan:
status set-keys create-strategies create-account test-order flatten
replay replay-live replay-all replay-regen scan replay-export
resync-canon auto-gapper premarket-fade
The operations are monitor status, keys set, strategy from-watchlist, users create, trade flatten, strategy resync-canon, gapper auto *, gapper premarket-fade, replay *, scan movers and dataset export. For example, idiotproof replay regen re-renders the replay archive HTML from the ReplayRun rows already in SQL; it does not fetch new market data. Publishing that archive is a separate manual step.
The Hub is Windows-Service-ready: idiotproof hub install-service (or tools/install-hub-service.ps1) registers IdiotProof.Hub to run as you, because LocalDB and the Data Protection ring are per-user. A SQL sp_getapplock leader lease guarantees only one instance trades per database; a second instance waits in standby and takes over automatically if the leader dies. Shutdown is graceful via IHostApplicationLifetime.
IdiotProof.Blazor is a Blazor Server app (interactive server components) on ASP.NET Core, using MindAttic.Authentication 6.0.0 (Argon2id + pepper, sessions, MFA scaffolding, not ASP.NET Core Identity; it sends security alerts such as password changed, repeated failed sign-ins and new-device sign-in over SMTP from the Vault Notifications bucket when that is complete, and logs a startup warning otherwise; self-service password reset is the library's token flow: /forgot-password emails a single-use link to MindAttic:Auth:Reset:PublicBaseUrl + /account/reset, which is https://localhost:65025 in appsettings.Development.json and the web app's own azurewebsites.net origin in infra/main.bicep) and EF Core 10 against SQL Server. Pages under Components/Pages/:
| Component | Purpose |
|---|---|
Strategies.razor |
Front door: every saved strategy for the signed-in user, active toggle, live N/M progress badge, edit and delete, expand to see the rendered flowchart and raw script. |
StrategyBuilder.razor |
Strategy editor with Guided, Script and Describe tabs and a live StrategyBuilderRenderer preview. |
Gapper.razor |
Queue and dial in gapper strategies; "From a transcript" free-text extraction via GapperInterpreter. |
Learn.razor |
The Learning Center: seeded articles with inline live-rendered strategy examples via [[...]] wikilinks (WikilinkParser, WikiContent). |
Research.razor |
Ranked "Today's High-Impact Events" feed over ResearchScanner output; a collapsed Advanced panel keeps the older manual ticker and paste flow. |
Options.razor |
The manual options section (/options, see BIBLE §4.4), deliberately separate from the strategy pipeline. Sandbox, Paper and Live account switch; options chain (calls, strike, puts) with per-cell breakeven and a real-vs-hype (intrinsic vs extrinsic) meter; a plain-English order ticket; open option positions with a real/hype split bar and an informational take-profit callout. Live orders reuse the 5-minute password elevation, and the ticket locks itself per action when the account's options_trading_level does not allow it. Host logic in Services/OptionsTradingService.cs. |
Backtest.razor |
Backtest a saved strategy over one day (Alpaca bars when keyed, Mock otherwise): summary, P&L and a per-candle condition table. |
ActivityLog.razor |
Audit trail viewer. |
ApiKeys.razor |
Per-user broker and Claude key entry, written to that user's own Vault entries (never SQL, never the host LLM keyring); stored keys are never sent back to the page, only a masked prefix (blank field = keep). |
Settings.razor |
Preferences, theme and the six RiskGuardian limits. |
Login.razor, Register.razor, ForgotPassword.razor, ResetPassword.razor, ForgotUsername.razor |
Auth flows against MindAttic.Authentication; ForgotPassword (MaForgotPassword) and ResetPassword (MaResetPassword, at /account/reset) are the self-service reset. |
LiveChart.razor |
Live chart surface. |
Shared components (Components/Shared/): AccountSummaryBar, GlossaryModal, LogsBadge, StrategyBlueprintViz, StrategyBuilderRenderer, ToastContainer. Hubs/TradingHub.cs is a SignalR hub, and Auth/AuthService.cs wraps the auth stack for the UI.
- The user types a ticker, a title and a plain-English description.
StrategyScriptGeneratorbuilds a system prompt by reflecting onStrategyBuilderandConditions, so the prompted verb catalog cannot drift from what compiles (IP-LAW-4), and sends it to Claude throughLegionClientin a single call.- The generated IdiotScript is parsed by
WikilinkParser.ParseScriptinto aStrategyDefinitionand rendered live byStrategyBuilderRenderer. - Save writes the row to SQL with a UUIDv7 id, paper by default.
The Alpaca palette is the only theme today (--brand #FFCD00, --green #00C853, --red #EF4444, ...), scoped under :root[data-theme="alpaca"] in wwwroot/css/_theme-alpaca.css. New themes drop in as additional _theme-{name}.css files plus a stylesheet link in Components/App.razor; components reference CSS custom properties, never raw colors, so switching is a single attribute flip.
The AccountPill mirrors Alpaca's UI: label, type (Paper or Live) and masked account ID. Live accounts render with a red outline, paper accounts with the brand-yellow outline. Credentials come from the shared MindAttic broker keyring, overlaid onto AppSettings at startup.
IdiotProof.ResearchScanner (Program.cs + ScanPassRunner) runs one scan pass and exits. It is fired on a cadence by a MindAttic.Scheduler (WatchDog) job and is decoupled from both the Monitor's trading loop and the Blazor request lifecycle (BIBLE §4.4):
- It sweeps watchlist tickers plus a rotating batch of the tracked universe (
TickerUniverseServiceandTrackedTicker, refreshed daily from Alpaca's asset list). EdgarService,Form4ParserandCorporateActionDetectorpull real SEC filing content (Form 4 insider transactions, 8-K item-code triage) rather than boilerplate summaries.RegulatoryScannerpolls the Federal Register for SEC and SRO notices, has an LLM triage out routine noise, and persists substantive ones as macroResearchClaimrows.CatalystExtractorcomposes a deterministic, sober sentence per claim instead of trusting a single LLM-written paragraph.SignificanceScorercombines magnitude, confidence, history, source trust, recency and watchlist membership into a 0 to 100 score that the Research feed sorts by.OutcomeBackfillServicefetches real historical prices to mark older claims Realized or Disproven, closing the loop between a claim and what the market did.
dotnet run --project IdiotProof.ResearchScanner| Env var | Default | Notes |
|---|---|---|
IDIOTPROOF_RESEARCHSCAN_BATCHSIZE |
300 | Tickers swept per pass beyond the watchlist. |
IDIOTPROOF_RESEARCHSCAN_DAYSBACK |
2 | Lookback window per source per pass. |
IDIOTPROOF_RESEARCHSCAN_REGULATORY_HOURS |
24 | Minimum hours between regulatory-scan cadences. |
IdiotProof follows two conventions shared across MindAttic projects:
- Shared keyrings live in Roaming (
%APPDATA%\MindAttic\<Subsystem>\providers.json):LLM(owned byMindAttic.Legion),Brokers(owned by IdiotProof:alpaca-paperandalpaca-live) andSecurity(owned byMindAttic.Authentication: pepper, bootstrap token). - Per-app state lives in Local (
%LOCALAPPDATA%\MindAttic\<AppName>\).
legion.json at the repo root configures the LLM voter panel and judge (the Monitor's LLM gate runs on it, see What one pass does), and is copied next to the Blazor, Monitor and ResearchScanner binaries:
{
"voters": ["claude", "openai", "gemini", "deepseek"],
"judge": "claude",
"tier": "high"
}A candidate fire is high-stakes, so every keyed provider on the panel votes before the Risk Guardian sees it. All LLM traffic routes through MindAttic.Legion and all LLM credential reads through MindAttic.Vault; no feature code calls a provider SDK directly.
dotnet build IdiotProof.slnx
dotnet test IdiotProof.slnxCounts from the last full Debug run (2026-10-31, .NET 10 SDK, each project run on its own); 37,319 tests, all green:
| Project | Passed | Failed | Notes |
|---|---|---|---|
IdiotProof.Strategies.Tests |
34,823 | 0 | DSL round-trip, backtester, gapper lifecycle, canonical-JSON contract, plus exhaustive combinatorial matrix classes (StrategyPermutationMatrixTests, StrategyThreeWayAndMatrixTests, ConditionalBlockOverridePermutationTests, ...) that expand to tens of thousands of generated cases |
IdiotProof.Operations.Tests |
1,603 | 0 | Every [Operation] (account, positions, orders, options, market, crypto, reference, strategy, gapper, replay, research, diary, keys, monitor, docs, streaming) against scripted Alpaca handlers and in-memory databases; catalog completeness; Alpaca coverage map |
IdiotProof.Core.Tests |
460 | 0 | TransactionGateway and the money-path laws (IP-LAW-12/13), Monitor gateway parity, Vault-only keys, research services, repositories |
IdiotProof.Cli.Tests |
104 | 0 | argv/flags, Hub client, help, exit codes, hub start/stop, public-docs privacy scan |
IdiotProof.Engine.Tests |
94 | 0 | RiskGuardian gate, SupervisedLoop resilience, WorkspaceManager, options pricing, AppSettings overlay |
IdiotProof.UI.Tests |
62 | 0 | Options presenter, option position view, options glossary |
IdiotProof.Brokers.Tests |
52 | 0 | AlpacaApi, BrokerRouter Sandbox default, Sandbox fills, options/equity-short/crypto wire formats (+3 [Explicit] real-paper options tests) |
IdiotProof.Monitor.Tests |
47 | 0 | Long/short order shapes, PremarketFadeScanner, BeBexDecayScanner, AutoGapperScanner, ReplayFeatures (+2 [Explicit] real-paper order tests, see below) |
IdiotProof.Indicators.Tests |
36 | 0 | Indicator math, candlestick detectors, ADX Wilder-seed regression |
IdiotProof.Blazor.Tests |
26 | 0 | Web-only seams: order-path guard, owner/Claude-key adapters, API Keys page |
IdiotProof.Mcp.Tests |
12 | 0 | Tool generation from the catalog, Hub forwarding, MCP resources |
The large IdiotProof.Strategies.Tests count is deliberate: the project generates exhaustive matrices over phase, condition and branch combinations rather than hand-writing each case.
The [Explicit] tests talk to the real Alpaca paper account and never run in a plain dotnet test. Run them by name. They read keys from the alpaca-paper entry in %APPDATA%\MindAttic\Brokers\providers.json, are skipped when that entry is missing, and refuse to run against any host other than paper-api.alpaca.markets. Every order they place is priced so it cannot fill and is cancelled right away.
dotnet test IdiotProof.Brokers.Tests --filter FullyQualifiedName~AlpacaPaperOptionsIntegrationTests
dotnet test IdiotProof.Monitor.Tests --filter FullyQualifiedName~AlpacaPaperShortIntegrationTestsWhat the 2026-10-03 run against the paper account showed: Alpaca accepts an equity order that carries both side and position_intent (the Monitor's sell_to_close exit was accepted, then cancelled). The 1-share sell_to_open short was rejected with 403 "account is not allowed to short", because the paper account has shorting disabled and under $2,000 of equity. That test reports Inconclusive until shorting is enabled on the account.
cd tests/IdiotProof.Cypress
npm install
npm run open # interactive
npm run ci # headless Chrome; uses CYPRESS_BASE_URL or defaults to https://localhost:5001Nine specs in cypress/e2e/ cover a smoke test, strategy authoring and the save/activate round-trip, API-key masking and the live danger modal, Vault-backed AI generation, sample-strategy round-trips, backtest replay, the live N/M condition-progress badge, the options section and live-mode strategies. They run deterministically against IDIOTPROOF_FAKE_LLM=1 (the FakeLlmHandler test seam intercepts Legion calls server-side) but need a live server to count as proven end to end.
run.ps1: publishes the Monitor (C:\Apps\IdiotProof\Monitor\) and the web app (C:\Apps\IdiotProof\Blazor\, with arun.batthat serves onhttp://localhost:5100), and writesC:\Apps\IdiotProof\run.bat, which the desktop shortcut points at. Each component is republished only when its source (its project plus every referenced project) is newer than its deployed exe, so an unchanged run is a sub-second no-op.-LauncherOnlyonly rewrites the launcher.-Forcedoes a clean bin/obj rebuild;-Launchstarts both afterwards.run.batopens a menu: 1) Deploy & Run runsrun.ps1 -Force(falling back to the existing build if the redeploy fails or the repo is missing), 2) Run skips the redeploy; either way it then starts the Monitor and the web app and opens the browser.zzz_Export.bat,zzz_Backup.bat: local convenience wrappers.tools/azure-provision.mddocuments one-time infrastructure steps;tools/seed-*.sqlseed example strategies.tools/codex.ps1: the documentation-canon tool (see Documentation).idiotproof.cmdandidiotproof(Git Bash): the CLI launchers; add the repo root to PATH to typeidiotproofanywhere.
Export.ps1 is a generic source-export utility, not IdiotProof logic; its header still calls itself a "Unity project" exporter, a leftover from the template it was copied from. It walks the repo, hashes every matching file and writes one flat text bundle (ExportedScripts.txt in the current directory): a JSON manifest (path, SHA-256, byte count, line count) followed by a delimited block per file, handy for pasting a codebase into an LLM context window.
# From the repo root; exports every .cs file by default
powershell -NoProfile -ExecutionPolicy Bypass -File Export.ps1Configuration lives at the top of the script:
$includeExtensionsdefaults to.csonly; Unity-era extensions are present but commented out.$excludeDirsskipsLibrary,Temp,Logs,Obj/obj,.git,.vs,Build(s)andPackages. Build output under each project is still walked unless it matches one of those literal names, so point it at a narrower$searchPathor clean first if you want a lean bundle.$assetsOnlyis$falseby default (the whole repo is scanned, not just anAssets/folder).
IdiotProof/
├── IdiotProof.slnx / .sln ← Active solution (slnx is canonical; .sln kept for older tooling)
├── legion.json ← Legion voter panel (high tier)
├── Export.ps1 ← Generic repo → text-bundle exporter
├── AGENTS.md, CLAUDE.md ← Project rules for AI tooling
├── README.md ← You are here
├── docker-compose.yml / infra/ ← Azure deployment scaffolding (predates the current tree; verify before relying on it)
│
├── IdiotProof.Models/ ← Domain DTOs
├── IdiotProof.Shared/ ← RiskGuardian, IndicatorSnapshot, options math
├── IdiotProof.Indicators/ ← Pure indicator math + CandlestickPatterns
├── IdiotProof.Scripting/ ← IdiotScript DSL
├── IdiotProof.Strategies/ ← IStrategy, DslStrategy, IndicatorSnapshotBuilder, GapperExitEvaluator, backtester
├── IdiotProof.DataFeeds/ ← IMarketDataFeed (Alpaca REST + streaming, Mock)
├── IdiotProof.Brokers/ ← Alpaca + Sandbox + IBrokerClient + BrokerRouter
├── IdiotProof.Engine/ ← DI root, AppSettings overlay chain, SupervisedLoop
├── IdiotProof.Core/ ← Shared service graph
│ ├── Data/, Migrations/ ← AppDbContext, entities, EF migrations
│ ├── Services/ ← StrategyScriptGenerator, LlmVotingService, repositories, research pipeline
│ ├── Trading/ ← TransactionGateway, OrderIntent, ConfirmationStore, AlpacaApiFactory
│ ├── Monitoring/ ← MonitorWorker, leader lease, scanners
│ └── Hosting/ ← AddIdiotProofCore / Trading / Monitor
├── IdiotProof.Operations/ ← [Operation] classes by family (Account, Orders, Market, Options, Crypto, ...)
├── IdiotProof.Hub/ ← The Hub (loopback API + Monitor host)
├── IdiotProof.Cli/ ← idiotproof command line
├── IdiotProof.Mcp/ ← MCP server (one tool per operation)
├── IdiotProof.Blazor/ ← Web app (a front door over Core)
│ ├── Components/Pages/ ← Strategies, StrategyBuilder, Gapper, Learn, Research, Options, ...
│ ├── Components/Shared/ ← AccountSummaryBar, StrategyBuilderRenderer, ...
│ └── wwwroot/ ← css/_theme-alpaca.css, data/gapper-profiles.json, user-guide.htm
├── IdiotProof.Settings/ ← Portable settings-tab Razor Class Library (IP-T335)
├── IdiotProof.UI/ ← Shared Razor Class Library (Options components, Tooltip/Term)
├── IdiotProof.Monitor/ ← Shim: starts the Hub; operator subcommands forward to Hub operations
├── IdiotProof.ResearchScanner/ ← One-shot research sweep console
├── IdiotProof.*.Tests/ ← NUnit projects (Engine, Indicators, Strategies, Brokers, Core, Operations, Cli, Mcp, Blazor, Monitor, UI)
├── docs/ ← Codex canon (BIBLE.md, AMENDMENTS.md, USER_STORIES.md), generated references, agent/, images/
├── run.ps1 ← Publish to C:\Apps\IdiotProof\ (writes run.bat)
├── idiotproof.cmd, idiotproof ← CLI launchers
├── .mcp.json ← Registers the idiotproof MCP server
├── tools/ ← codex.ps1, azure-provision.md, seed-*.sql
└── tests/
└── IdiotProof.Cypress/ ← End-to-end UI tests (Cypress 13, 9 specs)
- IdiotScript: the fluent DSL (
Stock.Ticker("NVDA").RequireAdxAbove(20)...Build()) that expresses a strategy as six lifecycle phases. - Phase: one of the six fixed stages every strategy walks: Setup, Filters, Entry, Order, Risk, Exit.
- Condition: a single boolean check (
IsAboveVwap(),OnReclaim(9)) composed with.And(),.Or()and.Not(). - Gate: one of the three pre-fire checks: condition match, LLM voter quorum, Risk Guardian.
- Risk Guardian:
IdiotProof.Shared.Risk.RiskGuardian, the final pre-trade veto; it can block regardless of strategy or LLM consensus. - Monitor: the unattended 24/7 evaluator and executor (
MonitorWorker), hosted by the Hub. - Hub:
IdiotProof.Hub, the one local process every command and transaction goes through. - Operation: one
[Operation("noun verb")]class; it is a CLI command, an MCP tool and a docs entry at once. - Confirmation code: the single-use 6-character code a Live action returns; nothing Live happens until it is presented.
- SupervisedLoop: the fault-tolerant tick loop the Monitor runs its per-pass work under.
- Voter panel or Legion: the multi-LLM quorum (
legion.json) that approves or rejects a generated script or a candidate fire, viaMindAttic.Legion. - ConditionProgress: the SQL row (
N/M, first failing verb) the Monitor upserts every pass and the Strategies page polls for live badges. - Sandbox broker: the always-registered simulated broker (instant fills, in-memory position book), the safe default in
BrokerRouter. - Gapper: a stock gapping up in premarket versus the previous close; the flagship trade is to buy in the 4 AM window and sell before the 9:30 bell.
- Gapper profile: a dialable template (gap %, volume ratio, price band, entry window, stops, giveback, arm and sell-by times, notional) stored in
IdiotProof.Core/Catalogs/gapper-profiles.json, cloned and tuned per ticker on the Gapper tab. - Peak giveback: the momentum-rollover exit: sell once price gives back N% of the run from entry to the post-entry peak; armed from a configured ET time.
- Previous close: the prior trading day's official close, the reference for gap %. Gap conditions fail closed without it.
- Research claim: one
ResearchClaimrow, a catalyst or portent extracted from a filing, news article or regulatory notice, with sentiment, magnitude, timing and a significance score. - Macro claim: a
ResearchClaimwithIsMacro = true, a regulatory or exchange-rule event that is not about one company. - Significance score: the 0 to 100 value
SignificanceScorercomputes per claim; the Research feed sorts by it. - Tracked ticker: a cached
TrackedTickerrow (symbol, exchange, latest price) in the research scanner's universe, refreshed daily from Alpaca's asset list. - BrokerMode: the per-strategy routing choice (Paper, Live or Sandbox) that overrides the global account's paper/live flag for that strategy.
These are verified in code or canon; the living version is docs/BIBLE.md section 7 ("Active frontier"), and the canon wins if they disagree.
- Options are manual-only. No option legs in the strategy schema, no IV or Greeks conditions, no options-aware
RiskGuardianmath, no multi-leg spreads; the Monitor never fires an options order. Both Alpaca accounts are at options level 3; place + cancel is proven against the real paper account, but the fill-and-close half of a real paper round-trip (IP-US-U10) is still open.sp-index-events.jsonis hand-maintained. - Shorts are verified on the Sandbox broker and in the wire format. Against the real Alpaca paper account, the short entry has not yet been accepted: the account cannot short (shorting disabled, equity under $2,000). An accepted
sell_to_openand a filled short covered bybuy_to_closeare still open (IP-US-K16). - There is no Polygon feed: only
AlpacaDataFeed,AlpacaStreamingClient,MockDataFeedandSwitchableMarketDataFeedexist.docker-compose.ymlandinfra/still reference aPolygonApiKeyvariable and asrc/IdiotProof.Blazor/Dockerfilepath that does not exist; verify that scaffolding before relying on it for a real deploy. UserPreferences.OpenStrategyTabsis an unused column awaiting removal in a migration.- The Learning Center and Backtest pages are built but not yet proven by a Cypress run (Epics I and J are partial).
ScriptParseris intentionally a tolerant, regex-driven parser; a Roslyn-based parser with exact line and column diagnostics is planned (IP-US-H1).
docs/ carries the authoritative, versioned canon under the MindAttic Codex convention; this README is the build, run and tour layer.
- docs/BIBLE.md (L0): what IdiotProof is and is not, the architecture canon, the project laws (
IP-LAW-n), verified build and test state, and the full glossary. - docs/AMENDMENTS.md (L1): pending decisions not yet folded into the bible; normally empty.
- User stories (L2): stories
IP-US-<Epic><n>, each marked done only with a citing NUnit or Cypress test. Epics include Risk Guardian, the Monitor loop, DSL and backtesting, indicator math, web authoring, Gapper, replay and ML dataset, the research scanner and options. - docs/BIBLE.digest.md: generated by
tools/codex.ps1 digest; never hand-edit. - docs/CLI_COMMANDS.md and docs/MCP_TOOLS.md: every command and MCP tool, generated from the operation catalog; never hand-edit.
- docs/ALPACA_COVERAGE.md: every Alpaca endpoint and the command that reaches it.
- docs/agent/IDIOTPROOF_PROTOCOL.md: how an assistant drives IdiotProof (bootstrap, exit codes, Live confirmation etiquette, intent to command map); docs/agent/operation-catalog.json is the machine-readable catalog and docs/agent/cli-hub-backlog.json the Epic W backlog as filed.
- The docs site, mindattic.github.io/IdiotProof, publishes this README and
docs/on every push tomain(the shared MindAttic.Agency DocFX workflow). - AGENTS.md: instructions for AI agents working in this repo; agents driving the app start with
idiotproof agent bootstrap.
Rules of engagement: a fact lives in exactly one layer and is referenced by ID, not by line number; after editing canon, run powershell -File tools/codex.ps1 doctor (it must exit 0); mark a story done only when a test or build proves it.
This repository has no license file. All rights reserved.
Part of MindAttic — see more projects at github.com/mindattic. Related: MindAttic.Legion (LLM voter panel), MindAttic.Vault (credential keyrings), MindAttic.Authentication (sign-in stack).


