
A trading platform built like an institution. Run by one person.
A trading platform built like an institution. Run by one person.
A production-grade trading platform with real-time execution, portfolio tracking, a Discord-native AI companion, and Stripe billing.
The challenge
Most trading tools fall into one of two categories: consumer apps so simplified they're useless for serious traders, or enterprise platforms so complex they require an ops team to configure. There was no middle ground for the technically sophisticated individual trader who wanted full control without the overhead.
Nexural was built to fill that gap — a production-grade fintech platform with real-time execution, portfolio tracking, a Discord-native AI companion, and a subscription billing layer that doesn't break on webhook retries.
The challenge: building a system at this scale — 185 relational database tables, 69 API endpoints, real-time market data ingestion, and a Stripe integration that needs to survive every edge case — without a dedicated backend team, a QA department, or a six-month runway for architecture review.
How we built it
PostgreSQL on Supabase with Row-Level Security for multi-tenant isolation and real-time subscriptions via Supabase channels for live portfolio updates. Backend: FastAPI (Python) — async-first, typed with Pydantic, organized into domain-bounded modules. Frontend: Next.js with server components for data-heavy pages and client components for interactive trading UI.
Real-time via WebSocket connections for live price feeds; Supabase realtime for portfolio state synchronization. Stripe with idempotent webhook handlers, subscription lifecycle management, and metered feature gating. Discord bot backed by OpenAI GPT-4 with tool-calling for portfolio queries, market commentary, and alert management.
The philosophy: don't build features until the foundation is right. The first two weeks were data modeling — no UI, no API, just entity-relationship design, understanding where the RLS policies needed to live, and mapping every Stripe event to a database state.
System map
How the pieces talk to each other.
Selected screens
Real product surfaces from the engagement — not stock illustrations.

Datasets dashboard — 47 sources, RLS-isolated, real-time ingestion telemetry on every row.
What it actually looks like
Architecture diagrams, CI runs, and dashboards from the engagement — not stock illustrations.
What shipped
185 PostgreSQL tables with full RLS policy coverage. 69 REST/WebSocket API endpoints (FastAPI). Real-time portfolio dashboard (Next.js + Supabase realtime). Trade execution interface with order management. Discord AI bot: portfolio queries, alerts, natural-language market commentary.
Stripe billing: subscription tiers, metered usage, trial periods, webhook idempotency. 61 test suites: unit, integration, E2E, contract, and security tests. CI/CD pipeline with GitHub Actions — no PR merges without passing gates.
The Stripe webhook layer is built on idempotency keys and event deduplication — a pattern now templated into micro-saas-starter on GitHub. The Discord bot uses function-calling to query the live portfolio API, meaning it responds to "how is my AAPL position" with real data, not hallucinated commentary.
Results
Platform operational: live, stable, serving active users. Zero billing incidents since launch — the idempotent webhook architecture holds. AI bot handling 200+ natural-language portfolio queries per week.
61 test suites provide regression coverage for all critical paths. Architecture patterns extracted into 3 open-source templates used in subsequent projects.
A single engineer, with the right architecture discipline and AI-assisted development workflow, can build and maintain a system at this complexity level. The 185-table schema isn't a vanity number — it's a data architecture that needed to be right before anything was built on top of it.
Available
- Database schema overview (anonymized)
- Webhook idempotency pattern documentation
- Discord bot architecture diagram
- CI/CD pipeline configuration template
Talk to people on this work.
No fabricated quotes. Reference contacts are shared during discovery, with both parties' consent.
Engineering lead
Worked alongside on production trading systems for 5+ years. Available for technical reference calls — code quality, on-call discipline, incident behavior.
Founder
Engaged Sage Ideas for a Ship + Operate combination. Willing to talk about scope discipline, timeline accuracy, and what handoff actually looked like.
“185 tables isn't a vanity number. It's the data model that needed to be right before anything else could be built on top of it.”
What almost happened.
Every project has near-misses. Decisions that, if we'd kept going, would have shipped a hole. The list below is the diff between the version that almost made it to prod and the version that did.
Inline excerpts.
Trimmed, but real. These are the patterns that made the system survive Stripe retries, multi-tenant queries, and a Discord bot that won't hallucinate positions.
# webhook_handler.py — production excerpt
@router.post("/stripe/webhook")
async def handle_stripe(req: Request, db: Db):
event = stripe.Webhook.construct_event(
await req.body(),
req.headers["stripe-signature"],
STRIPE_WEBHOOK_SECRET,
)
# idempotency: insert-or-noop on (event_id) primary key
inserted = await db.fetchval("""
INSERT INTO stripe_events (event_id, type, payload, status)
VALUES ($1, $2, $3, 'pending')
ON CONFLICT (event_id) DO NOTHING
RETURNING event_id
""", event["id"], event["type"], event.data.object)
if inserted is None:
# we've seen this event. ack and exit.
return {"ok": True, "replay": True}
# side-effect runs exactly once, inside a transaction
async with db.transaction():
await process_event(event, db)
await db.execute(
"UPDATE stripe_events SET status='processed' WHERE event_id=$1",
event["id"],
)
return {"ok": True}-- migrations/2026_03_rls_portfolios.sql
ALTER TABLE portfolios ENABLE ROW LEVEL SECURITY;
CREATE POLICY portfolios_owner_select
ON portfolios FOR SELECT
USING (owner_id = auth.uid());
CREATE POLICY portfolios_owner_modify
ON portfolios FOR ALL
USING (owner_id = auth.uid())
WITH CHECK (owner_id = auth.uid());
-- Even an authenticated client running raw SQL via PostgREST
-- cannot read another user's row. Verified in 14 contract tests.// bots/discord/tools.ts — production excerpt
export const tools = [
{
type: 'function',
function: {
name: 'get_position',
description: 'Fetch a live position for the calling user',
parameters: {
type: 'object',
properties: { symbol: { type: 'string' } },
required: ['symbol'],
},
},
},
] as const
export async function execute(tool: ToolCall, ctx: BotCtx) {
if (tool.function.name === 'get_position') {
const { symbol } = JSON.parse(tool.function.arguments)
// Real API call — same auth, same RLS as the web app.
return await api.get('/positions/' + symbol, { token: ctx.userToken })
}
throw new Error('unknown tool')
}