A trading intelligence platform where AI is constrained by deterministic
analytics: Wilson-bounded statistics, agentic copilots that never compute a number
themselves, multimodal chart analysis and a user-owned knowledge graph.
The journal is one module, not the product.
✓GROUNDED AI✓LOCAL-FIRST✓GRAPHRAG✓FASTAPI + REACT◈WEB PLATFORM IN PROGRESS
MOST TRADING SOFTWARE ANSWERS ONE QUESTION · WHAT HAPPENED?
Why did it happen?
ANSWERED BY
GraphRAG multi-hop retrieval over your own history, ranked by the win-rate swing a
factor actually causes.
What can I learn?
ANSWERED BY
An agentic coach that queries the journal mid-conversation and quotes the analytics
layer verbatim, never its own arithmetic.
What should I improve?
ANSWERED BY
Co-occurrence and cohort analysis that isolates the setup, session and liquidity
combinations costing you the most R.
How confident is that?
ANSWERED BY
A 95% Wilson interval and a confidence tier on every single bucket, deciding what the
platform is even allowed to claim.
Most trading journals answer one question: what happened?
Very few answer why it happened, and almost none distinguish
between meaningful statistical patterns and random noise.
A trader might believe a strategy has a 73% win rate, while ignoring that the
statistic comes from only 11 trades. Spreadsheets don't warn you. Generic AI
chatbots make it worse, because they'll happily narrate confidence the data can't support.
NexTrade was built around a single philosophy, enforced in every module,
every SQL macro and every AI prompt of the codebase:
Never let a number, human or AI, imply more confidence
than the sample size supports.
73% WIN RATE
FROM 11 TRADES · NOISE, NOT SIGNAL
⚠ NOT ENOUGH DATA · VERDICT REFUSED
NexTrade attaches a 95% Wilson confidence interval to every win-rate stat, labels
each bucket Insufficient / Tentative / Reliable,
and its pre-trade gate refuses to issue a verdict on insufficient samples. It says
"not enough data" rather than guess.
◆ CAPABILITIES
Powerful and Extensive
One platform covering the full loop: log, analyze, consult,
validate and remember, with nothing leaving your machine.
Runs entirely on your machine
🗄DuckDB journal
🧩Ollama runtime
🕸Obsidian vault
⚡FastAPI core
☁Web platform next
📊
Rigorous Analytics
Wilson intervals, confidence tiers and break-even-aware win rates on every bucket:
equity curves, monthly cohorts and strategy intelligence built on one canonical engine.
🧠
Agentic AI Coach
A LangGraph tool loop that queries your journal mid-conversation, then delivers a full
audit: mistakes, patterns and concrete recommendations, every number quoted verbatim.
👁
Vision Copilot
A multimodal analyst that reads annotated chart screenshots for support and resistance,
trend and sentiment, then auto-journals trades by strict extraction, never invention.
🛡
Pre-Trade Gate
Before you enter, query your own history for that exact setup, session and direction.
Pure Wilson-interval math, no LLM, and it refuses to judge a thin sample.
NEXTRADE
◆ PRODUCT SHOWCASE
See it working
Real captures of the running app,
from the landing dashboard to the AI copilots. Every screen below is the actual product.
NEXTRADE · FULL PRODUCT TOUR
LANDING · TRADE WITH PRECISION
◆ 01 · THE PLATFORM
Trade with precision. Analyze with AI.
A single-page React 19 app behind a FastAPI backend. The dashboard hub loads lazily,
Plotly rides in its own chunk keeping the initial bundle around 312 KB, and every tab
reads from one cached, version-keyed analytics API.
Filter 250+ logged trades by year, month, result, setup, position, liquidity or hour,
then drill into any single trade: entry/exit chart, full setup context, RR and result.
Screenshots are validated by magic bytes on upload, and external chart links are fetched
through an SSRF-guarded proxy.
MULTI-DIMENSIONAL FILTERSMAGIC-BYTE VALIDATION
PRE-TRADE GATE
◆ 03 · PRE-TRADE GATE
Ask your own history before you enter
Describe the trade you're about to take (setup, session, direction, liquidity) and the
gate compares that combination's 95% win-rate interval against your whole journal. The
verdict is pure math, no LLM involved, and on a thin sample it answers
"not enough data" instead of guessing.
A tilt guardrail watches loss streaks the same way.
RULE-BASED VERDICTWILSON SEPARATIONTILT GUARDRAIL
AI RISK MANAGER · NEMOTRON-3 · LOCAL
◆ 04 · TEXT COACH
An analyst grounded in your numbers
The AI Risk Manager runs a LangGraph tool loop over the journal: mid-conversation it calls
its query_journal tool, agentic RAG
over a hand-built entity graph, to pull the exact stat block it needs, then answers a full
audit. Every number it quotes arrives verbatim from the analytics layer.
LANGGRAPH TOOL LOOPAGENTIC RAG4× SPEED CAPTURE
VISION AI CANVAS · GEMMA VISION
◆ 05 · VISION COPILOT
An AI that reads your charts
Upload a trade screenshot and the multimodal copilot annotates it: support and resistance,
trend detection and sentiment, each scored and explained, grounded in the same journal.
It also auto-journals: a strictly extractive path prefills the Journal Builder from a
screenshot, dropping anything not clearly visible rather than inventing it.
MULTIMODALAUTO-JOURNALING4× SPEED CAPTURE
◆ CHAPTER · THE KNOWLEDGE GRAPH
Years of behavior, visible
This is the part of NexTrade that stops being a journal.
Every trade you log becomes a permanent node in a personal knowledge graph you own as plain
Markdown, and the graph rewrites itself after every session without a single click.
OBSIDIAN · AI TRADING HUB · GRAPH VIEW · GENERATED, NOT DRAWN
◆ WHAT BECOMES A NODE, AUTOMATICALLY
TRADESETUPSESSIONLIQUIDITYDIRECTIONMARKETWEEKLY AI DEBRIEFBREAK-EVEN PAYOFF NOTE
Nobody drew this
Not one node in that image was placed by hand. The vault exporter walks the journal,
writes a Markdown note per trade, and links it to its dimension notes: setup, session,
liquidity, direction, market. Obsidian's graph view does the rest.
The clusters you can see are real behavioral structure. A dense knot around one setup
means most of your history runs through it, which is exactly the bucket the statistics
engine has enough sample size to speak about with confidence.
The sync contract
Continuous export into a user's personal vault is a data-loss problem disguised as a
feature. NexTrade solves it with a manifest: every file the sync creates is
recorded, and the sync is only ever allowed to touch files on that list.
✕Never writes outside the namespaced AI Trading Hub folder
✕Never overwrites or deletes a file it did not create
✕Never regenerates a weekly debrief: history is deliberately kept off the manifest, so it cannot be rewritten by a later sync
Why Obsidian, not a custom graph
Building a force-directed graph UI would have cost weeks and produced something worse
than a tool that already exists, is free, is offline, and that users may already run.
Exporting to Markdown instead bought a world-class graph explorer for the price of a
file writer, and it left the data in a format that outlives the app. If NexTrade
disappeared tomorrow, the vault would still open.
The cost is honest and stated in the trade-off ledger below: the sync is one-way, so
edits made in Obsidian do not flow back into the journal.
◆ ONE ENGINE
Every consumer, same math
A single canonical statistics module grounds the dashboards,
both AI copilots, the pre-trade gate and the knowledge graph. One implementation,
imported everywhere, never copied.
bucket_stats
CANONICAL ENGINE
◆ ARCHITECTURE
The path a number takes
Everything on the left of this diagram is deterministic. The
model only ever sits at the end of the pipeline, and by the time it speaks, every figure it
can quote has already been computed, bounded and labeled.
The interesting property of this diagram is the boundary.
Retrieval, statistics and the pre-trade verdict all resolve before the model is invoked,
so the LLM's job is language, never arithmetic. Remove the model entirely and the platform
still answers the quantitative questions.
◆ LAYER DETAIL
Open any layer
Seven layers, each with the decision that shaped it.
REACT 19 + VITE
~16,500 lines JSX · 30 components+
Single-page frontend: dashboard hub with lazy-loaded Plotly charts,
journal builder, annotation canvas, dual AI chat panels. A canonical invariants.js
mirrors the backend's win-rate math, and a Vitest parity suite catches any drift between
the two implementations.
FASTAPI
~5,800 lines Python · 46 tests green+
Async API with sync journal endpoints on purpose, so DuckDB/pandas work
runs in worker threads and never blocks the AI event loop. A lifespan-managed background
loop ticks every 5 minutes: vault sync when data changed, backups every 24h, weekly AI
debriefs. That replaced three manual buttons with a self-driving system which survives
dev-server reloads without leaking tasks.
ANALYTICS ENGINE
graph.py + derived.py · canonical math+
bucket_stats and wilson_ci exist exactly once.
Break-even trades are never wins but stay in the denominator; every stat carries its
confidence tier and 95% interval. Monte-Carlo drawdown projections are deterministically
seeded from the data so both AIs quote identical numbers. Grounding tests assert the exact
figures that reach the LLM prompts.
GRAPHRAG
hand-built retrieval, DB-agnostic+
A traversable entity graph over plain trade dicts: multi-hop retrieval
ranked by win-rate swing, 2-way co-occurrence blocks (Setup×Session), 3-way deepening, and
cohort trend blocks. Because it's DB-agnostic, the same module grounds the DuckDB-fed Vision
AI and the payload-fed text coach, then powers the pre-trade gate and tilt guardrail with
zero LLM involvement.
DUCKDB
embedded analytical store, single file+
One journal.duckdb holds multi-journal trades, vision strategies and AI
feedback telemetry. Single-writer by design: one shared connection with cursor-per-call
threading, writes serialized through one lock, live backups via COPY FROM DATABASE,
and SQL macros mirroring the win/lose/BE predicates so the invariant holds in both the
Python and SQL paths.
OLLAMA
nemotron-3-ultra · gemma4:31b · all local+
Both copilots run against a local Ollama server through LangGraph with
MemorySaver checkpointing. The system prompt is injected exactly once per session; history
trimming never drops it. If the model rejects tool calling, the coach gracefully falls back
to tool-less mode. A live eval suite runs golden questions against the real model.
OBSIDIAN VAULT
the user-owned knowledge graph+
One-way export into a namespaced AI Trading Hub folder: trades,
dimensions, per-journal break-even payoff notes and weekly debriefs as linked Markdown.
Manifest-tracked so sync only overwrites or deletes files it created, which makes the
user's own notes untouchable. Debriefs live outside the manifest on purpose: history must
never be regenerated.
◆ CONFIDENCE TIERS
What the system will say, and won't
Every bucket is labeled by sample size, and the label decides
what the platform and its AIs are allowed to claim.
INSUFFICIENT
< 30TRADES IN BUCKET
Wilson interval shown✓
Stat displayed in UI✓
Keep / avoid verdict✕
AI may recommend✕
Pre-trade gate passes✕
TENTATIVE
30 – 99TRADES IN BUCKET
Wilson interval shown✓
Stat displayed in UI✓
Keep / avoid verdict✓
AI may recommend✓
Monte-Carlo risk model✓
RELIABLE
≥ 100TRADES IN BUCKET
Wilson interval shown✓
Stat displayed in UI✓
Keep / avoid verdict✓
AI may recommend✓
Used as journal baseline✓
◆ ENGINEERING DECISIONS
Chosen, not defaulted
01Canonical statistical engine
One shared implementation computes every metric used across the app and both AI pipelines.
"Import it, never copy it" is written into the module docs, and where code can't be shared,
the duplication is tested instead of ignored.
02AI grounding contract
The language models never compute statistics. Every numerical insight is retrieved from the
analytics layer before it reaches the LLM, and dedicated grounding tests assert the exact
numbers entering each system prompt.
03Integrate, don't rebuild
Instead of spending weeks on a custom force-directed graph UI, the knowledge graph exports
to Obsidian, a tool that already does graph exploration exceptionally well.
04The DuckDB race fix
A real concurrency bug, since DuckDB connections aren't thread-safe, solved with a
cursor-per-call pattern on one shared connection rather than reaching for a heavier database
engine. Pragmatic, correct, documented as a permanent rule.
05Explicit cache invalidation
Read caches are keyed on a data version that every mutating endpoint must bump, never on
TTLs. A little discipline traded for hard correctness guarantees.
06Lifecycle-aware automation
Background maintenance is wired through FastAPI's modern lifespan context and built to
survive dev-server reload cycles without leaking orphaned tasks. Idempotent ticks mean a
down Ollama just retries next tick.
◆ SECURITY
Hardened before it was needed
Unusually thorough for a single-user local tool. The kind of defenses
that usually appear only after an incident are here pre-emptively, and they are exactly what a
hosted version will need on day one.
SSRF-GUARDED PROXYEvery hop of the image proxy (initial URL,
og:image target, HTTP redirects) must resolve to a public address before fetching.
MAGIC-BYTE VALIDATIONUploads are validated by file signature,
never by the declared Content-Type header.
LOCALHOST-ONLYNo auth exists yet, so the backend never binds
wider than 127.0.0.1. Authentication is the first gate the web release has to clear.
THREAD-SAFE DUCKDBCursor-per-call access on a single shared
connection; all writes serialized through one lock.
CONTROLLED CORSA hardcoded allowlist of the two Vite ports,
credentialed CORS disabled, and internal errors replaced by generic 500s so stack traces never
leak to the client.
◆ CHALLENGES
The hard parts
Making AI statistically honest
The hardest problem wasn't integrating an LLM. It was ensuring the AI never presents
confidence beyond what the sample size justifies. The answer became a system: Wilson intervals
on every stat, three confidence tiers enforced in prompts and data structures,
grounding tests that assert the exact numbers entering the model, and a live eval suite
running golden questions against the real model after every prompt change.
Synchronizing knowledge without destroying user data
A one-way sync engine that continuously exports to Obsidian while preserving the user's own
notes required manifest-based tracking and a strict separation between generated and permanent
content. Weekly debriefs are deliberately excluded from the manifest so history can never be
regenerated or deleted by a later sync.
Keeping two AI systems consistent
The text coach reads the request payload; the vision copilot reads the database directly.
They stay consistent because both are grounded by the same DB-agnostic statistics module, and
deterministic Monte-Carlo seeding means both quote identical risk numbers across sessions.
Same shape, different retrieval strategy, one source of truth.
◆ ROADMAP
Built in tiers, not in one bang
Seven scoped increments, each shipped with tests before the next
began. That habit kept a solo build from collapsing under itself, and phase 08 is where
NexTrade leaves the desktop.
◈ NEXT MILESTONE
From local-first to the open web
Everything you see above runs on a single machine today. The next release takes NexTrade
online as a hosted platform, so a trader can open a browser, sign in and get the same
grounded analytics without installing anything. The engineering rules do not change:
the statistics stay canonical, the AI stays grounded, and your journal stays yours.
Hosted appManaged deployment, nothing to install
Accounts & isolationAuth, per-user data boundaries, encrypted at rest
Hybrid inferenceManaged models, or keep pointing at your own Ollama
Local mode staysOffline, single-machine use remains fully supported
PHASE 01
01Phase
Trading Journal
The DuckDB-backed multi-journal core.
✓Manual logging + Excel import
✓Column-level date repair
✓Screenshot pipeline & proxy
PHASE 02
02Phase
Statistics Engine
Where the philosophy became code.
✓Wilson confidence intervals
✓Three-tier confidence ladder
✓Break-even math as invariant #1
PHASE 03
03Phase
GraphRAG
Retrieval over your own history.
✓Entity graph + multi-hop retrieval
✓Co-occurrence, triples, cohorts
✓Pre-trade gate & tilt guardrail
PHASE 04
04Phase
AI Agents
The LangGraph era.
✓Nemotron coach + tool loop
✓Gemma vision copilot
✓Grounding tests & live evals
PHASE 05
05Phase
Knowledge Graph
Trades become a living note graph.
✓Obsidian vault export
✓Manifest-tracked, non-destructive
✓Weekly AI debriefs
PHASE 06
06Phase
Automation NOW
Buttons replaced by a self-driving loop.
✓Vault sync on data change
✓Rolling 24h backups
✓Lifespan-managed scheduler
PHASE 07
07Phase
Reporting
Evidence you can hand to someone else.
✓PDF audit dossiers
✓News briefings with correlation
✓Every figure quoted verbatim
IN PROGRESS
08Phase
Web Platform NEXT
From one machine to any browser.
→Hosted multi-user deployment
→Accounts, auth and per-user isolation
→Managed inference beside local Ollama
→Cloud journal sync, offline still supported
BACKLOG
09Phase
Engineering Debt
The honest list.
→TypeScript across the frontend
→Splitting the 1,673-line main.py
→CI pipeline for the test suites
◆ ENGINEERING TRADE-OFFS
Every choice cost something
A stack list says what you used. This says what each decision bought,
what it cost, and what was done about the cost. Nothing here is free, and pretending otherwise
is how architectures rot.
DUCKDB
Embedded analytical store, one portable file.
BOUGHT
Columnar speed for analytical queries with zero server administration, and a journal that is a single file the user can copy, back up or delete.
COST
Connections are not thread-safe and the engine is single-writer. It is the wrong choice for distributed or high-concurrency writes.
HANDLED
Cursor-per-call on one shared connection, all writes serialized through a single lock, live backups via COPY FROM DATABASE. The hosted release will need a different write path, and that is already scoped.
OLLAMA · LOCAL INFERENCE
Both copilots run on the user's own machine.
BOUGHT
Privacy by construction and offline capability. A trading journal is among the most sensitive files a person owns; it never leaves the device, and there is no per-token cost.
COST
Answer quality and latency are bounded by local hardware, and not every local model reliably supports tool calling.
HANDLED
A graceful tool-less fallback when the model rejects tool calls, and phase 08 adds hybrid inference: managed models optional, your own Ollama still supported.
GRAPHRAG · HAND-BUILT
An entity graph over trades, not a vector store.
BOUGHT
Multi-hop retrieval over structured relationships: setup × session × liquidity, ranked by the win-rate swing a factor causes. No embedding infrastructure, and results traceable back to specific trades.
COST
No semantic similarity, so free-text questions outside the schema retrieve nothing, and the traversal logic is code I have to maintain rather than a library.
HANDLED
Traversal is capped and whitelisted, every block is grounded in canonical stats, and it is kept DB-agnostic so the same module serves the DuckDB-fed vision copilot and the payload-fed text coach.
OBSIDIAN AS THE GRAPH UI
Integrate, don't rebuild.
BOUGHT
A world-class graph explorer for the cost of a Markdown exporter, plus a data format that outlives the application.
COST
A hard dependency on someone else's app for that experience, and the sync is one-way: edits made in the vault do not flow back into the journal.
HANDLED
Manifest-tracked, non-destructive export, so the vault stays valid Markdown with or without NexTrade and the user's own notes are untouchable.
SYNC ENDPOINTS IN AN ASYNC API
Deliberately un-idiomatic FastAPI.
BOUGHT
Declaring journal endpoints sync pushes DuckDB and pandas work into worker threads, so a heavy analytics query can never block the event loop the AI streams on.
COST
Throughput on those routes is bounded by the threadpool, and it reads as a mistake until you know why it is there.
HANDLED
Only the blocking routes are sync; every AI path stays async. The reasoning is written into the module docs so the next person does not "fix" it.
VERSION-KEYED CACHE, NOT TTL
Correctness over convenience.
BOUGHT
A read cache that is never stale. Analytics are keyed on a data version, so a user never sees a number that contradicts the trade they just logged.
COST
Every mutating endpoint must remember to bump the version. That is discipline, not a guarantee, and a forgotten bump is a silent bug.
HANDLED
One documented invariant, one helper to call, and tests that assert the version moves. A TTL would have hidden the same bug behind a timer instead.
JAVASCRIPT, NOT TYPESCRIPT
The debt I chose knowingly.
BOUGHT
Velocity. Solo, across eight phases, with the hard correctness problems living in the Python analytics layer rather than in the UI.
COST
No compile-time safety across ~16,500 lines of JSX, and refactors lean on tests instead of the type checker.
HANDLED
A canonical invariants.js mirrors the backend win-rate math and a Vitest parity suite fails if the two implementations drift. The TypeScript migration is on the roadmap as debt, not sold as a feature.
LOCALHOST-ONLY, NO AUTH
The safest auth system is the one you don't ship early.
BOUGHT
No credential handling, no session management, no auth surface to get wrong while the product was still single-user.
COST
The app cannot be exposed as it stands, which is precisely why it is the first gate the web release has to clear.
HANDLED
The backend never binds wider than 127.0.0.1, and the defenses that usually come after an incident (SSRF-guarded proxy, magic-byte upload validation, allowlisted CORS, generic 500s) were built before they were needed.
◆ THE REST OF THE STACK
Chosen for a reason
TECHNOLOGY
WHY IT WAS CHOSEN
LangGraph
Structured tool-calling workflows with explicit state management and
checkpointing: an auditable agent loop instead of an opaque chain.
React 19 + Vite
A fast SPA with lazy loading and code-splitting: three chart
libraries on demand while the initial bundle stays ~312 KB.
Pytest + Vitest
Grounding tests, invariant parity suites and live LLM evals,
testing the hard parts (what reaches the AI), not just chasing coverage.
◆ WHAT I LEARNED
The real lesson
Building NexTrade taught me that integrating an LLM is the easy part. Designing the
system around it so that it stays statistically sound, maintainable, secure and genuinely
useful is where the software engineering actually begins. Every interesting decision in this
case study came from that one constraint: never let a number, human or machine, imply more
confidence than the sample size supports.
0LINES OF PYTHON
0LINES OF JSX
0TESTS GREEN
0WRITTEN INVARIANTS
0AI COPILOTS
0% LOCAL
◆ TRANSMISSION
Evidence over intuition
Want a walkthrough of the code, the architecture, or the grounding test
suite? Open a channel. The engine is always listening.